无形的执行顺序(dispatch_semaphore_t,dispatch_group_async)及其与不同调度队列types组合使用
我只是花了一些时间在晚上玩GCD,特别是dispatch_semaphore_t
因为我从来没有使用它。 从来没有需要。
所以我写了下面这个testing:
- (void)viewDidLoad { UIView *firstView = [[UIView alloc] initWithFrame:(CGRect){{0, 0}, self.view.frame.size.width/4, self.view.frame.size.width/5}]; firstView.backgroundColor = [UIColor purpleColor]; [self.view addSubview:firstView]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { for (long i = 0; i < 1000; i++) { sleep(5); dispatch_async(dispatch_get_main_queue(), ^ { firstView.layer.opacity = ((i%2) ? 0: 1); }); } }); dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group1 = dispatch_group_create(); dispatch_group_async(group1, queue1, ^ { sleep(3); NSLog(@"dispatch group 1"); }); dispatch_group_notify(group1, queue1, ^ { NSLog(@"dispatch notify 1"); }); dispatch_async(myQueue, ^ { for(int z = 0; z < 10; z++) { NSLog(@"%i", z); sleep(1); } dispatch_semaphore_signal(mySemaphore); }); dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER); NSLog(@"Loop is Done"); }
如果我运行上面,输出将是:
0
1
2
派遣组1
派遣通知1
3
4
五
6
7
8
9
循环完成
之后, firstView
出现在屏幕上(在semaphore
之前,整个屏幕是黑色的),最后得到执行:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { for (long i = 0; i < 1000; i++) { sleep(5); dispatch_async(dispatch_get_main_queue(), ^ { firstView.layer.opacity = ((i%2) ? 0: 1); }); } });
只有在semaphore
完成后循环才会交替opacity
。
1.)
所以,似乎我必须等到dispatch_semaphore
完成任何UI事情发生之前完成其工作。
但是 :
看起来像dispatch_group_t
与dispatch_semaphore
同时运行,如上面的输出(即,1,2,3 …)所示。
???
2.)
如果我改变上面的for loop
使用: dispatch_async(dispatch_get_main_queue(), ^
代替:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
,
即使在semaphore
完成后,屏幕上也不会显示任何信息。
怎么会这样???
3.)
此外,如果我将semaphore
改为以下,而不是像上面那样使用全局队列:
dispatch_async(dispatch_get_main_queue(), ^ { for(int z = 0; z < 10; z++) { NSLog(@"%i", z); sleep(1); } dispatch_semaphore_signal(mySemaphore); });
只有dispatch_group
发生; 没有别的地方/得到执行,而不是在上面的for loop
,包括用户界面。 没有。
4)
因此,除了我在上面指出的,我能做些什么,为了使semaphore
不阻塞我的用户界面和我的其他进程,让我的用户界面和其他进程做他们的事情?
如上所述,为什么将semaphore
队列的types从全局变为主要会导致屏幕上不显示任何内容,除了dispatch_group
之外,甚至不会执行循环。
5.)
如果我将semaphore
更改为:
dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(1); //1 instead of 0 (zero)
一切(即循环和用户界面)运行立即和NSLog(@"Loop is Done");
也立即显示,这告诉我信号量不在这里等待:
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER); NSLog(@"Loop is Done");
???
我花了整个晚上试图找出这个,但无济于事。 我希望有很好的GCD知识的人可以在这方面给我启发。
首先要做的是:一般来说,不要阻塞主队列。 关于不阻塞主队列的规则适用于dispatch_semaphore_wait()
和sleep()
(以及任何同步调度,任何组等待等)。 您不应该在主队列上执行任何潜在的阻塞调用。 如果你遵循这个规则,你的用户界面不应该变成不响应。
您的代码示例和后续问题可能似乎暗示了组和信号量之间的混淆。 调度组是跟踪一组调度块的一种方式。 但是你没有在这里利用调度组的特点,所以我可能会build议把它们从讨论中删除,因为它与有关信号量的讨论无关。
另一方面,调度信号量只是一个线程向另一个等待信号的线程发送信号的机制。 不用说,事实上你已经创build了一个信号量并通过这个信号发送了信号,不会影响你的任何调度任务(不pipe是否分组),除非有问题的代码碰巧调用dispatch_semaphore_wait
。
最后,在后面的一些例子中,你尝试了信号量发送多个信号,或者改变创build信号量时提供的初始计数。 对于每个signal
,你通常需要一个相应的wait
。 如果你有十个信号,你需要十个等待。
所以,让我们以一种方式来说明信号量,其中主队列(以及UI)永远不会被阻塞。 在这里,我们可以在两个独立的同时运行的任务之间发送十个信号,后者更新UI:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); // send 10 signals from one background thread dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ for (NSInteger i = 0; i < 10; i++) { NSLog(@"Sleeping %d", i); sleep(3); NSLog(@"Sending signal %d", i); dispatch_semaphore_signal(semaphore); } NSLog(@"Done signaling"); }); // and on another thread, wait for those 10 signals ... dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ for (NSInteger i = 0; i < 10; i++) { NSLog(@"Waiting for signal %d", i); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Got signal %d", i); // if you want to update your UI, then dispatch that back to the main queue dispatch_async(dispatch_get_main_queue(), ^{ // update your UI here }); } NSLog(@"Done waiting"); });
诚然,这并不是一个非常有用的信号量的例子,但它说明了你可以在理论上如何使用它们。 在实践中,你不得不使用信号量,对于大多数业务问题,还有其他更优雅的编码模式。 如果你描述你想要做什么,我们可以告诉你如何最好地实现它。
至于传递给dispatch_semaphore_create
的非零值的例子,用于控制对某些有限资源的访问。 在这个例子中,让我们假设你有100个任务要运行,但是你不想在任何给定的时间运行超过5个任务(例如,你正在使用networking连接(这是有限的),或者每个操作占用你想避免在任何给定时间运行超过五次的内存)。 那么你可以做一些事情:
// we only want five to run at any given time dispatch_semaphore_t semaphore = dispatch_semaphore_create(5); // send this to background queue, so that when we wait, it doesn't block main queue dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSInteger i = 0; i < 100; i++) { // wait until one of our five "slots" are available dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // when it is, dispatch code to background queue dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"starting %d", i); // to simulate something slow happening in the background, we'll just sleep sleep(5); NSLog(@"Finishing %d", i); // when done, signal that this "slot" is free (please note, this is done // inside the dispatched block of code) dispatch_semaphore_signal(semaphore); }); } });
再一次,这不是信号量的一个很好的例子(在这种情况下,我通常使用带有NSOperationQueue
的maxConcurrentOperationCount
),但是它举例说明了为什么你要为dispatch_source_create
使用一个非零值。
你已经提出了一些关于团体的问题。 我认为组织与你自己的信号量无关。 例如,如果要在所有任务完成时运行一个代码块,则可以使用一个组。 所以这里是上面例子的一个变种,但是当该组中的所有其他任务都完成时,使用dispatch_group_notify
来做一些事情。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // or create your own concurrent queue dispatch_semaphore_t semaphore = dispatch_semaphore_create(5); dispatch_group_t group = dispatch_group_create(); // send this to background queue, so that when we wait, it doesn't block main queue dispatch_async(queue, ^{ for (NSInteger i = 0; i < 100; i++) { // wait until one of our five "slots" are available dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // when it is, dispatch code to background queue dispatch_group_async(group, queue, ^{ NSLog(@"starting %d", i); // to simulate something slow happening in the background, we'll just sleep sleep(5); NSLog(@"Finishing %d", i); dispatch_semaphore_signal(semaphore); }); } dispatch_group_notify(group, queue, ^{ NSLog(@"All done"); }); });