Очереди отправки: как узнать, запущены ли они и как их остановить

Я просто играю с GCD, и я написал игрушку CoinFlipper.

Вот метод, который переворачивает монеты:

- (void)flipCoins:(NSUInteger)nFlips{ // Create the queues for work dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL); // Split the number of flips into whole chunks of kChunkSize and the remainder. NSUInteger numberOfWholeChunks = nFlips / kChunkSize; NSUInteger numberOfRemainingFlips = nFlips - numberOfWholeChunks * kChunkSize; if (numberOfWholeChunks > 0) { for (NSUInteger index = 0; index  0) { dispatch_async(queue, ^{ NSUInteger h = 0; NSUInteger t = 0; flipTheCoins(numberOfRemainingFlips, &h, &t); dispatch_async(mainQueue, ^{ self.nHeads += h; self.nTails += t; }); }); } } 

Как вы видете; Я разбиваю количество переворотов на большие куски, переворачивая их в фоновом режиме и обновляя свойства в основной очереди. Свойства проверяются оконным controllerом, и пользовательский интерфейс обновляется с результатами выполнения.

Я просмотрел Руководство по программированию параллелизма и документы GCD, и хотя есть способ приостановить очередь, нет способа остановить их и удалить все запущенные и не запущенные объекты.

Я хотел бы иметь возможность подключить кнопку «Стоп», чтобы отменить переключение после его запуска. С NSOperationQueue я могу наблюдать свойство operationCount чтобы узнать, запущен ли он, и cancelAllOperations для удаления блоков в очереди.

Я просмотрел Руководство по программированию параллелизма и документы GCD, и хотя есть способ приостановить очередь, нет способа остановить их и удалить все запущенные и не запущенные объекты.

Так :-

  1. Как узнать, все еще ожидают ли блоки, добавленные в очередь?
  2. Как отменить блоки, которые еще не запущены?
  3. Я новичок в материалах GCD, так что я делаю это правильно?

Это очень часто задаваемый вопрос при программировании с помощью GCD.

Короткий ответ заключается в том, что GCD не имеет API отмены для очередей. Обоснование:

  1. управление памятью станет значительно более сложным, потому что данный блок может быть ответственным за бесплатное () при заданном распределении памяти. Всегда управляя блоком, GCD гарантирует, что управление памятью остается легким.
  2. Практически невозможно остановить рабочий блок без повреждения.
  3. Большинство кода, требующих логики отмены, уже отслеживают это состояние в частных структурах данных.

Учитывая все эти случаи, гораздо эффективнее и мощнее писать код следующим образом:

 dispatch_async(my_obj->queue, ^{ bool done = false; // do_full_update() takes too long, therefore: while ( !my_obj->cancelled && !done ) { done = do_partial_update(my_obj); } }); 

О, и чтобы узнать, закончила ли очередь выполнение всех заблокированных блоков, ваш код может просто выполнить пустой блок с синхронным API:

 dispatch_sync(my_obj->queue, ^{}); 

Как упоминалось в комментариях, лучший способ узнать, когда ваша работа будет выполнена, – использовать группы отправки. Отправляйте все блоки в группу, а затем вы можете добавить обработчик завершения в группу. Как только работа будет завершена, запустится блок завершения.

 dispatch_group_t myGroup = dispatch_group_create(); dispatch_group_async(myGroup, my_obj->queue, ^{ bool done = false; while ( !my_obj->cancelled && !done ) { done = do_partial_update(my_obj); } }); dispatch_group_notify(myGroup, my_obj->queue, ^{ NSLog(@"Work is done!"); dispatch_release(myGroup); }); 

Как только все ваши блоки будут завершены, группа будет пуста и вызовет блок уведомлений. Оттуда вы можете обновить интерфейс и т. Д.

Удачи и приятного времяпровождения!

Как определить, работает ли

 BOOL dispatch_queue_is_empty(dispatch_queue_t queue) { dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); dispatch_async(queue, ^{ dispatch_group_leave(group); }); int64_t maxWaitTime = 0.00000005 * NSEC_PER_SEC; BOOL isReady = dispatch_group_wait(group, maxWaitTime) == 0; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ dispatch_group_wait(group, DISPATCH_TIME_FOREVER); dispatch_release(group); }); return isReady; } 

Тест в приложении

 dispatch_queue_t queue = dispatch_queue_create("test", 0); NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO"); dispatch_async(queue, ^{ for(int i = 0; i < 100; i++) { NSLog(@"... %i", i); } }); NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO"); 

результат

 Is empty YES Is empty NO ... 0 ... 1 ... 2 ... 3 ... 4 ... 5 ... 6 ... 7 ... 8 ... 9 

Значение по умолчанию для переменной maxWaitTime может быть изменено до желаемого результата.

Если у вас есть очередная диспетчерская очередь или параллельная очередь отправки, вот код, который может сделать то же самое.

 BOOL __block queueIsEmpty = false; dispatch_barrier_async (_dispatchQueue, ^{ queueIsEmpty = true; }); while (!queueIsEmpty) { int i = 0; // NOOP instruction } // At this point your queue should be empty. 
  • iVar свойство, доступ через self?
  • Сравнение двух NSDates и игнорирование временной составляющей
  • Использование хеша MD5 в строке в cocoa?
  • Запустите NSRunLoop в командной строке программы Cocoa
  • Процедура отправки сообщений iOS 6 Facebook заканчивается ошибкой «remote_app_id не соответствует сохраненному id»
  • Можно ли отключить плавающие заголовки в UITableView с помощью UITableViewStylePlain?
  • Есть ли способ указать позицию / индекс аргумента в NSString stringWithFormat?
  • NSArray слабых ссылок (__unsafe_unretained) для объектов под ARC
  • objective C: Как вы можете повернуть текст для UIButton и UILabel?
  • Проблема с кодом: строка формата не является строковым литералом
  • Как размыть сцену в SpriteKit?
  • Давайте будем гением компьютера.