派遣队列:如何判断他们是否在运行以及如何阻止他们

我只是玩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; }); }); } } 

如你看到的; 我打破了大块翻转的数量在后台翻转,并更新主队列中的属性。 窗口控制器正在观察属性,用运行结果更新UI。

我已经查看了“并发编程指南”和GCD文档,虽然有一种挂起队列的方法,但是没有办法阻止它们,并且删除所有排队和不运行的对象。

我想能够挂起一个“停止”button来取消翻转一旦开始。 使用NSOperationQueue我可以观察到operationCount属性是否正在运行, cancelAllOperations删除排队的块。

我已经查看了“并发编程指南”和GCD文档,虽然有一种挂起队列的方法,但是没有办法阻止它们,并且删除所有排队和不运行的对象。

所以: –

  1. 如何判断我已添加到队列中的块是否仍在等待?
  2. 如何取消尚未运行的块?
  3. 我是GCD的新手,所以我做对了吗?

用GCD编程时这是一个半常见的问题。

简单的答案是GCD没有取消队列的取消API。 理由:

  1. 内存pipe理将变得非常复杂,因为给定的块可能负责免费分配给定的内存。 通过始终运行该块,GCD确保内存pipe理仍然容易。
  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); }); 

一旦所有块都完成,组将会是空的并触发通知块。 从那里,你可以更新UI等

祝好运并玩得开心点!

如何判断是否正在运行

 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; } 

要在应用程序中testing

 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 

variablesmaxWaitTime的默认值可以调整为想要的结果。

如果你有一个串行调度队列或一个并发调度队列,这里有一个代码可以做同样的事情。

 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.