Очереди отправки: как узнать, запущены ли они и как их остановить
Я просто играю с 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 < numberOfWholeChunks; index++) {
dispatch_async(queue, ^{
NSUInteger h = 0;
NSUInteger t = 0;
flipTheCoins(kChunkSize, &h, &t);
dispatch_async(mainQueue, ^{
self.nHeads += h;
self.nTails += t;
});
});
}
}
if (numberOfRemainingFlips > 0) {
dispatch_async(queue, ^{
NSUInteger h = 0;
NSUInteger t = 0;
flipTheCoins(numberOfRemainingFlips, &h, &t);
dispatch_async(mainQueue, ^{
self.nHeads += h;
self.nTails += t;
});
});
}
}
Как вы можете видеть; Я разбиваю количество переворотов на большие куски, переворачивая их в фоновом режиме и обновляя свойства в основной очереди. Свойства наблюдаются оконным контроллером, и пользовательский интерфейс обновляется с результатами выполнения.
Я просмотрел руководство по программированию Concurrency и документы GCD, и хотя есть способ приостановить очередь, нет способа остановить их и удалить все запущенные и не запущенные объекты.
Я хотел бы иметь возможность подключить кнопку "Стоп", чтобы отменить перелистывание после его начала. С помощью NSOperationQueue
я могу узнать свойство operationCount
, чтобы узнать, запущен ли он, и cancelAllOperations
для удаления блоков в очереди.
Я просмотрел руководство по программированию Concurrency и документы GCD, и хотя есть способ приостановить очередь, нет способа остановить их и удалить все запущенные и не запущенные объекты.
Итак: -
- Как узнать, все еще ждут ли блоки, добавленные в очередь?
- Как отменить блоки, которые еще не запущены?
- Я новичок в материалах GCD, так что я делаю это правильно?
Ответы
Ответ 1
Это вопрос, который часто возникает при программировании с помощью GCD.
Короткий ответ заключается в том, что GCD не имеет API отмены для очередей. Обоснование:
-
Менеджмент памяти
- станет значительно более сложным, поскольку данный блок может нести ответственность за бесплатное() при заданном распределении памяти. Всегда управляя блоком, GCD гарантирует, что управление памятью остается легким.
- Практически невозможно остановить рабочий блок без повреждения.
- Большинство кодов, требующих логики отмены, уже отслеживают это состояние в частных структурах данных.
Учитывая все эти случаи, гораздо эффективнее и мощнее писать такой код:
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);
});
Как только все ваши блоки будут завершены, группа будет пуста и вызовет блок уведомлений. Оттуда вы можете обновить интерфейс и т.д.
Удачи и получайте удовольствие!
Ответ 2
Как определить, работает ли
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
может быть изменено до желаемого результата.
Ответ 3
Если у вас есть очередная диспетчерская очередь или параллельная очередь отправки, вот код, который может сделать то же самое.
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.