Нужны некоторые разъяснения по поводу send_queue_create и RunLoop. Совместное использование RunLoop между ними

Я разрабатываю платформу iOS с Objective-C.

Я создаю dispatch_queue_t, используя dispatch_queue_create. И вызовите CFRunLoopRun(), чтобы запустить цикл выполнения в очереди.

Но похоже, что send_queue_t имеет общий RunLoop. В некоторые классы добавлен недопустимый таймер, и когда я вызываю CFRunLoopRun(), он зависал на моей стороне.

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.queue1 = dispatch_queue_create("com.queue1", DISPATCH_QUEUE_CONCURRENT);
    self.queue2 = dispatch_queue_create("org.queue2", DISPATCH_QUEUE_CONCURRENT);
}

- (IBAction)btnButtonAction:(id)sender {
    
    dispatch_async(self.queue1, ^{
        
        NSString *runloop = [NSString stringWithFormat:@"%@", CFRunLoopGetCurrent()];
        runloop = [runloop substringWithRange:NSMakeRange(0, 22)];
        NSLog(@"Queue1 %p run: %@", self.queue1, runloop);
        
        //NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1 target:self selector:@selector(wrongSeletor:) userInfo:nil repeats:NO];
        //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    });
    
    dispatch_async(self.queue2, ^{
        NSString *runloop = [NSString stringWithFormat:@"%@", CFRunLoopGetCurrent()];
        runloop = [runloop substringWithRange:NSMakeRange(0, 22)];
        NSLog(@"Queue2 %p run: %@", self.queue2, runloop);
        
        CFRunLoopRun();
    });
}

Некоторое время они используют один и тот же RunLoop:

https://i.sstatic.net/wGcv3.png

=====

Вы можете увидеть сбой, раскомментировав код NSTimer. NSTimer был добавлен в queue1, но он все еще работает при вызове CFRunLoopRun() в queue2.

Я прочитал описание вроде: нужны некоторые разъяснения по поводу очереди отправки, потока и NSRunLoop

Они рассказали, что: system creates a run loop for the thread. Но, по моему мнению, они используют RunLoop.

Для меня это печально, потому что я столкнулся с тем, что при вызове CFRunLoopRun() на производство происходят сбои.

Используйте dispatch_source для создания таймеров. Забудь про NSTimer

Cy-4AH 11.04.2024 15:59

Никогда не запускайте цикл выполнения в обратном вызове очереди отправки. Для цикла выполнения требуется выделенный поток, а очереди GCD не имеют выделенных потоков (за исключением основной очереди, которая всегда использует основной поток). Вам следует запускать цикл выполнения только в основном потоке или в созданном вами потоке. Очередь отправки не является потоком.

rob mayoff 11.04.2024 17:06

Спасибо всем, ваши слова очень много значат для меня.

Askri Nguyen 11.04.2024 18:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
103
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

вр; доктор

С этим кодом цикла выполнения существует несколько проблем. Но если бы целью было просто запустить таймер в очереди отправки, мы бы обычно использовали таймер источника отправки, что устраняло бы большую часть этой путаницы.


Типичный шаблон цикла выполнения предполагает создание выделенного потока, получение цикла выполнения для этого потока и вызов одного из методов «запуска» для вращения этого потока и обработки событий цикла выполнения, таких как события таймера. (Пример см. в пункте 2 этого ответа.)

Очередь отправки — это другой подход. Когда мы отправляем «рабочий элемент» в очередь отправки, эта очередь будет извлекать (случайный) рабочий поток из пула потоков, запускать этот рабочий элемент в этом рабочем потоке и, когда рабочий элемент будет выполнен, возвращать этого рабочего. поток в пул потоков, что делает его доступным для будущих отправок.

Это подводит нас к нескольким наблюдениям относительно вашего фрагмента кода:

  1. Отправка на queue1 не имеет смысла. Вы печатаете цикл выполнения, но не «запускаете» его, а просто завершаете, тем самым возвращая рабочий поток в пул потоков. Теперь этот поток доступен для повторного использования в будущих рассылках GCD. Это имело бы смысл только в том случае, если бы вы запускали этот цикл выполнения с помощью метода «run», тем самым привязывая этот поток к этому циклу выполнения и предотвращая повторное использование этого потока GCD.

  2. Использование параллельной очереди в этом контексте сбивает с толку. Запуск циклов вращается в заданном потоке. Создавать для этой цели параллельную очередь не имеет смысла. Какая работа будет выполняться одновременно в этой очереди, пока цикл выполнения выполняется в одном из рабочих потоков? Это предполагает концептуальное непонимание того, как работают циклы выполнения.

  3. Идея бесконечно вращаться в цикле выполнения в рабочем потоке GCD вообще опрометчива. Общая концепция очередей отправки заключается в том, что они достигают эффективности за счет извлечения существующего рабочего потока из пула, выполнения рабочего элемента и возврата этого потока в пул. Идея привязки рабочего потока на неопределенный срок, возможно, противоречит мотивирующей идее, лежащей в основе GCD. Вы можете это сделать, но, как правило, мы бы не привязывали рабочий поток GCD на неопределенный срок.

  4. Если вы академически интересуетесь тем, как работают циклы выполнения, это исследование подойдет. Но если вы намеревались просто запустить таймер в очереди отправки, тогда выполните циклы и NSTimer — это устаревший подход, предшествующий GCD. Таймер отправки был бы намного проще (см. ниже).


Если вы хотите запустить что-то в очереди отправки, мы обычно не используем NSTimer. Вместо этого мы бы использовали таймер отправки. Для этого вообще не требуется цикл выполнения.

В отличие от NSTimer нам нужно сохранить собственную строгую ссылку на таймер:

@property (nonatomic, strong) dispatch_source_t timer;

Затем, чтобы запустить таймер:

- (void)startTimer {
    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.timer", DISPATCH_QUEUE_SERIAL);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(self.timer, ^{
        // this is called when the timer fires
    });
    dispatch_resume(self.timer);
}

Чтобы остановить таймер, отпустите его:

- (void)stopTimer {
    self.timer = nil;
}

Спасибо, Роб. Я буду использовать его для таймера.

Askri Nguyen 11.04.2024 18:25

Спасибо, Роб. Я очень ценю ваше руководство.

Askri Nguyen 11.04.2024 18:50

Другие вопросы по теме