objective-c 调度队列:如何判断它们是否正在运行以及如何停止它们
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1550658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dispatch queues: How to tell if they're running and how to stop them
提问by Abizern
I'm just playing around with GCD and I've written a toy CoinFlipper app.
我只是在玩 GCD,我写了一个玩具 CoinFlipper 应用程序。
Here's the method that flips the coins:
下面是抛硬币的方法:
- (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;
});
});
}
}
As you can see; I'm breaking the number of flips into large chunks flipping them in the background and updating properties in the main queue. The properties are being observed by the window controller and an UI is updated with the running results.
如你看到的; 我将翻转次数分成大块,在后台翻转它们并更新主队列中的属性。窗口控制器正在观察这些属性,并使用运行结果更新 UI。
I've looked through the Concurrency Programming Guide and the GCD docs, and although there is a way to suspend a queue, there isn't a way to stop them, and remove all queued and not running objects.
我查看了并发编程指南和 GCD 文档,虽然有一种方法可以挂起队列,但没有一种方法可以停止它们,并删除所有排队和未运行的对象。
I'd like to be able to hook up a 'stop' button to cancel flipping once it's started. With NSOperationQueueI can observe the operationCountproperty to know if it's running, and cancelAllOperationsto remove queued blocks.
我希望能够连接一个“停止”按钮来取消翻转一旦开始。随着NSOperationQueue我可以观察operationCount属性知道它的运行,并cancelAllOperations删除排队块。
I've looked through the Concurrency Programming Guide and the GCD docs, and although there is a way to suspend a queue, there isn't a way to stop them, and remove all queued and not running objects.
我查看了并发编程指南和 GCD 文档,虽然有一种方法可以挂起队列,但没有一种方法可以停止它们,并删除所有排队和未运行的对象。
So :-
所以 :-
- How do I tell if blocks I've added to a queue are still waiting?
- How do I cancel blocks that haven't run yet?
- I'm new to the GCD stuff, so am I doing it right?
- 如何判断已添加到队列中的块是否仍在等待?
- 如何取消尚未运行的块?
- 我是 GCD 的新手,所以我做对了吗?
回答by Anonymous
This is a semi-common question when programming with GCD.
这是使用 GCD 编程时的一个半常见问题。
The short answer is that GCD does not have a cancelation API for queues. The rationale:
简短的回答是 GCD 没有队列的取消 API。理由:
- memory management would become vastly more complicated, because a given block might be responsible for free()ing a given allocation of memory. By always running the block, GCD ensures that memory management remains easy.
- It is practically impossible to halt a runningblock without corrupting state.
- Most code that needs cancellation logic is already tracking that state in private data structures.
- 内存管理将变得更加复杂,因为给定的块可能负责释放()给定的内存分配。通过始终运行块,GCD 确保内存管理仍然容易。
- 在不破坏状态的情况下停止正在运行的块实际上是不可能的。
- 大多数需要取消逻辑的代码已经在私有数据结构中跟踪该状态。
Given all of these cases, it is far more efficient and powerful to write code like this:
考虑到所有这些情况,编写这样的代码会更加高效和强大:
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);
}
});
Oh, and to know if a queue has finished running all of the enqueued blocks, your code can simply execute an empty block with the synchronousAPI:
哦,要知道队列是否已完成所有入队块的运行,您的代码可以简单地使用同步API执行一个空块:
dispatch_sync(my_obj->queue, ^{});
As mentioned in the comments, a better way of knowing when your work is done is to use dispatch groups. Dispatch all your blocks to the group and then you can add a completion handler to the group. Once the work is complete, the completion block will run.
正如评论中提到的,了解工作何时完成的更好方法是使用调度组。将所有块分派到该组,然后您可以向该组添加完成处理程序。一旦工作完成,完成块将运行。
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);
});
Once all of your blocks have completed, the group will be empty and trigger the notification block. From there, you can update UI, etc.
完成所有块后,该组将为空并触发通知块。从那里,您可以更新 UI 等。
Good luck and have fun!
祝好运并玩得开心点!
回答by hfossli
How to tell if is running
如何判断是否正在运行
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;
}
To test in app
在应用程序中测试
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");
Result
结果
Is empty YES
Is empty NO
... 0
... 1
... 2
... 3
... 4
... 5
... 6
... 7
... 8
... 9
The default value for the variable maxWaitTimecan be tweaked to wanted result.
maxWaitTime可以将变量的默认值调整为想要的结果。
回答by Kris Subramanian
If you have a serial dispatch queue OR a concurrent dispatch queue, here is a code that can do the same thing.
如果你有一个串行调度队列或一个并发调度队列,这里有一个可以做同样事情的代码。
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.

