Я разрабатываю платформу 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() на производство происходят сбои.
Никогда не запускайте цикл выполнения в обратном вызове очереди отправки. Для цикла выполнения требуется выделенный поток, а очереди GCD не имеют выделенных потоков (за исключением основной очереди, которая всегда использует основной поток). Вам следует запускать цикл выполнения только в основном потоке или в созданном вами потоке. Очередь отправки не является потоком.
Спасибо всем, ваши слова очень много значат для меня.





вр; доктор
С этим кодом цикла выполнения существует несколько проблем. Но если бы целью было просто запустить таймер в очереди отправки, мы бы обычно использовали таймер источника отправки, что устраняло бы большую часть этой путаницы.
Типичный шаблон цикла выполнения предполагает создание выделенного потока, получение цикла выполнения для этого потока и вызов одного из методов «запуска» для вращения этого потока и обработки событий цикла выполнения, таких как события таймера. (Пример см. в пункте 2 этого ответа.)
Очередь отправки — это другой подход. Когда мы отправляем «рабочий элемент» в очередь отправки, эта очередь будет извлекать (случайный) рабочий поток из пула потоков, запускать этот рабочий элемент в этом рабочем потоке и, когда рабочий элемент будет выполнен, возвращать этого рабочего. поток в пул потоков, что делает его доступным для будущих отправок.
Это подводит нас к нескольким наблюдениям относительно вашего фрагмента кода:
Отправка на queue1 не имеет смысла. Вы печатаете цикл выполнения, но не «запускаете» его, а просто завершаете, тем самым возвращая рабочий поток в пул потоков. Теперь этот поток доступен для повторного использования в будущих рассылках GCD. Это имело бы смысл только в том случае, если бы вы запускали этот цикл выполнения с помощью метода «run», тем самым привязывая этот поток к этому циклу выполнения и предотвращая повторное использование этого потока GCD.
Использование параллельной очереди в этом контексте сбивает с толку. Запуск циклов вращается в заданном потоке. Создавать для этой цели параллельную очередь не имеет смысла. Какая работа будет выполняться одновременно в этой очереди, пока цикл выполнения выполняется в одном из рабочих потоков? Это предполагает концептуальное непонимание того, как работают циклы выполнения.
Идея бесконечно вращаться в цикле выполнения в рабочем потоке GCD вообще опрометчива. Общая концепция очередей отправки заключается в том, что они достигают эффективности за счет извлечения существующего рабочего потока из пула, выполнения рабочего элемента и возврата этого потока в пул. Идея привязки рабочего потока на неопределенный срок, возможно, противоречит мотивирующей идее, лежащей в основе GCD. Вы можете это сделать, но, как правило, мы бы не привязывали рабочий поток GCD на неопределенный срок.
Если вы академически интересуетесь тем, как работают циклы выполнения, это исследование подойдет. Но если вы намеревались просто запустить таймер в очереди отправки, тогда выполните циклы и 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;
}
Спасибо, Роб. Я буду использовать его для таймера.
Спасибо, Роб. Я очень ценю ваше руководство.
Используйте
dispatch_sourceдля создания таймеров. Забудь проNSTimer