Abi*_*ern 33 cocoa multithreading objective-c grand-central-dispatch
我正在玩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;
});
});
}
}
Run Code Online (Sandbox Code Playgroud)
如你看到的; 我正在将翻转的数量分成大块,在后台翻转它们并更新主队列中的属性.窗口控制器正在观察属性,并使用运行结果更新UI.
我查看了并发编程指南和GCD文档,虽然有一种方法可以暂停队列,但是没有办法阻止它们,并删除所有排队和未运行的对象.
我希望能够连接一个"停止"按钮,一旦它开始就取消翻转.随着NSOperationQueue我可以观察operationCount属性知道它的运行,并cancelAllOperations删除排队块.
我查看了并发编程指南和GCD文档,虽然有一种方法可以暂停队列,但是没有办法阻止它们,并删除所有排队和未运行的对象.
所以: -
小智 49
在使用GCD编程时,这是一个半常见的问题.
简短的回答是GCD没有队列的取消API.理由:
鉴于所有这些情况,编写如下代码会更有效,更强大:
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);
}
});
Run Code Online (Sandbox Code Playgroud)
哦,要知道一个队列是否已经完成了所有排队的块的运行,你的代码可以简单地用同步 API 执行一个空块:
dispatch_sync(my_obj->queue, ^{});
Run Code Online (Sandbox Code Playgroud)
正如评论中所提到的,了解工作何时完成的更好方法是使用调度组.将所有块发送到组,然后您可以向组添加完成处理程序.工作完成后,将运行完成块.
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);
});
Run Code Online (Sandbox Code Playgroud)
完成所有块后,该组将为空并触发通知块.从那里,您可以更新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;
}
Run Code Online (Sandbox Code Playgroud)
在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");
Run Code Online (Sandbox Code Playgroud)
结果
Is empty YES
Is empty NO
... 0
... 1
... 2
... 3
... 4
... 5
... 6
... 7
... 8
... 9
Run Code Online (Sandbox Code Playgroud)
maxWaitTime可以将变量的默认值调整为所需结果.