Почему я не могу остановить таймер в последовательной очереди dispatch_async? - PullRequest
0 голосов
/ 13 сентября 2018

Это просто экспериментальный код, но я запутался, так как код не выполнялся, как я предполагал.

Код похож на:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.myQueue = dispatch_queue_create("com.maxwell.timer", NULL);
    dispatch_async(self.myQueue, ^{
        self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"Hey!");
        }];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    });
}

Теперь я получил вывод "Эй!" каждую секунду здесь нет проблем Я знаю, что в отправляемом потоке я должен явно запустить runloop.

Проблема возникла, когда я попытался остановить таймер.

- (void)stopTimer { 
    dispatch_async(self.myQueue, ^{
        [self.timer invalidate];
        self.Timer = nil;
    });
}

На самом деле код в блоке даже не будет выполнен!

Более того, если бы я использовал параллельную очередь здесь (dispatch_asyn(dispatch_get_global_queue(...), ^{...})), все было бы в порядке.

Вещи, которые я знаю: каждый раз, когда я dispatch_async, независимо от параллельной или последовательной очереди, код выполняется в другом потоке. Строго говоря, Я не сделал недействительным таймер в том же потоке, где я его добавил , но он сделал недействительным в параллельном потоке.

Итак, мой вопрос: почему он не был признан недействительным в последовательной очереди?

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Проблема в том, что у вас есть последовательная очередь, в которую вы звоните [[NSRunLoop currentRunLoop] run].Но вы не возвращаетесь с этого вызова (если в этом цикле выполнения есть таймеры и т.п.).Как указано в документации run :

Если к циклу выполнения не подключены входные источники или таймеры, этот метод завершается немедленно;в противном случае он запускает приемник в NSDefaultRunLoopMode, повторно вызывая runMode:beforeDate:.Другими словами, этот метод эффективно запускает бесконечный цикл, который обрабатывает данные из входных источников и таймеров цикла выполнения.

Это блокирует поток вашей последовательной очереди.Любой код, отправленный в эту очередь (например, ваша попытка аннулировать таймер), не будет работать, пока этот поток заблокирован.У вас есть «Catch 22».

Кроме того, если вы собираетесь настроить фоновый поток для запуска NSTimer, вам нужно создать свой собственный поток для этого, а неиспользуйте один из рабочих потоков GCD. См., например, https://stackoverflow.com/a/38031658/1271826. Но поскольку этот ответ продолжает описывать, предпочтительным методом для запуска таймеров в фоновом потоке являются таймеры диспетчеризации, позволяющие избавиться от множества манипуляций.нити и циклы выполнения.

0 голосов
/ 13 сентября 2018

Я думаю, что:

В последовательной очереди задача готова к выполнению, только если ее предшественник завершен.Здесь, поскольку запускается цикл запуска, который запускает таймер, задача отмены таймера ожидает (заблокирована).Таким образом, блок кода никогда не выполняется.

...