无形执行顺序(dispatch_semaphore_t,dispatch_group_async)及其与不同调度队列类型的结合使用 [英] Intangible Order of Execution (dispatch_semaphore_t, dispatch_group_async) and the Use of Them in Combination with Different Dispatch Queue Types
问题描述
我只是花了一些时间在晚上玩GCD,尤其是 dispatch_semaphore_t
,因为我从未使用它。从来没有必要。
I just took some time in the evening to play around with GCD, especially with dispatch_semaphore_t
because I never used it. Never had the need to.
所以我写了以下内容作为测试:
So I wrote the following as a test:
- (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");
}
如果我运行上述内容,输出将是:
If I ran the above, the output would be:
0
0
1
2
派遣组1
派遣通知1
3
4
5
6
7
8
9
循环已完成
在上述之后, firstView
出现在屏幕上(在信号量
之前,整个屏幕都是黑色),最后执行此操作:
After the above, firstView
appears on the screen (before semaphore
the whole screen was black) and finally this gets executed:
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);
});
}
});
仅 替代不透明度
在信号量
完成后循环运行。
Which only alternates the opacity
as the loop runs after semaphore
is done.
1。)
1.)
所以,我似乎要等到 dispatch_semaphore
在任何UI事件发生之前完成其工作。
So, it seems that I have to wait until dispatch_semaphore
finish to do its work before any UI thing takes place.
但是:
似乎 dispatch_group_t
与 dispatch_semaphore
同时运行,如上面的输出所示(即,1,2,3,......)。
It seems like dispatch_group_t
runs concurrently with dispatch_semaphore
as shown from the output above (i.e., 1, 2, 3, ....).
???
2。)
2.)
如果我将上面的 for loop
更改为使用: dispatch_async(dispatch_get_main_queue(),^
And if I change the for loop
in the above to using: dispatch_async(dispatch_get_main_queue(), ^
而不是:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0 ),^
,
即使在信号量
完成后,屏幕上也没有显示任何内容。
nothing gets shown on the screen even after semaphore
is done.
如何???
3。)
3.)
此外,如果我将信号量
更改为以下内容而不是使用如上所述的全局队列:
Furthermore if I change semaphore
to the following as opposed to using a global queue like in the above:
dispatch_async(dispatch_get_main_queue(), ^
{
for(int z = 0; z < 10; z++)
{
NSLog(@"%i", z);
sleep(1);
}
dispatch_semaphore_signal(mySemaphore);
});
仅 dispatch_group
采取plac Ë;没有别的地方/执行,而不是上面的 for loop
,包括UI。没什么。
Only dispatch_group
takes place; nothing else take places / get executed, not the for loop
in the above, including UI. Nothing.
4。)
4.)
除了我在上面指出的内容之外,我还能做些什么,以使信号量
不会阻止我的UI和我的其他进程,让我的UI和其他进程做他们的事情?
So besides what I pointed out in the above, what can I do, in order to make semaphore
not blocking my UI and my other process and just let my UI and the other processes do their thing?
如上所述,为什么更改信号量的队列类型
从全局到主要不会导致屏幕上显示任何内容,甚至循环也不会执行,除了 dispatch_group
?
And as mentioned above, why changing the type of queue for semaphore
from global to main would cause nothing to be shown on screen and even the loop would not execute except dispatch_group
?
5。)
5.)
如果我将信号量
更改为:
dispatch_semaphore_t mySemaphore = dispatch_semaphore_create(1);
//1 instead of 0 (zero)
一切(即for循环和UI)立即运行并且 NSLog(@Loop is Done);
也会立即显示,这告诉我信号量没有在这里等待:
Everything (i.e., both for loop and UI) runs immediately and NSLog(@"Loop is Done");
is displayed also immediately, which tells me that semaphore didn't wait here:
dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Loop is Done");
???
我花了整整一个晚上试图解决这个问题,但无济于事。
我希望有GCD知识的人可以在这方面给我启发。
I spent the whole evening trying to figure this out, but to no avail. I hope someone with great GCD knowledge can enlighten me on this.
推荐答案
首先要做的事情:作为一般规则,永远不应该阻止主队列。关于不阻塞主队列的规则适用于 dispatch_semaphore_wait()
和 sleep()
(以及任何同步调度,任何组等待等)。您永远不应该在主队列上执行任何这些可能阻塞的调用。如果您遵循此规则,您的UI永远不会变得无响应。
First things first: As a general rule, one should never block the main queue. This rule about not blocking the main queue applies to both dispatch_semaphore_wait()
and sleep()
(as well as any of the synchronous dispatches, any group wait, etc.). You should never do any of these potentially blocking calls on the main queue. And if you follow this rule, your UI should never become non-responsive.
您的代码示例和后续问题似乎表明组和信号量之间存在混淆。调度组是一种跟踪一组调度块的方法。但是你没有利用调度组的功能,所以我建议将它们从讨论中删除,因为它与关于信号量的讨论无关。
Your code sample and subsequent questions might seem to suggest a confusion between groups and semaphores. Dispatch groups are a way of keeping track of a group of dispatched blocks. But you're not taking advantage of the features of dispatch groups here, so I might suggest excising them from the discussion as it's irrelevant to the discussion about semaphores.
调度信号量另一方面,它只是一个线程将信号发送到另一个等待信号的线程的机制。不用说,你创建了一个信号量并通过该信号量发送信号的事实不会影响你的任何调度任务(无论是否分组),除非有问题的代码碰巧调用 dispatch_semaphore_wait
。
Dispatch semaphores are, on the other hand, simply a mechanism for one thread to send a signal to another thread that is waiting for the signal. Needless to say, the fact that you've created a semaphore and sent signals via that semaphore will not affect any of your dispatched tasks (whether to group or not) unless the code in question happens to call dispatch_semaphore_wait
.
最后,在您的一些后续示例中,您尝试让信号量发送多个信号或更改创建信号量时提供的初始计数。对于每个信号
,您通常需要相应的等待
。如果您有十个信号,则需要十次等待。
Finally, in some of your later examples you tried have the semaphore send multiple signals or changing the initial count to supplied when creating the semaphore. For each signal
, you generally want a corresponding wait
. If you have ten signals, you want ten waits.
因此,让我们以一种永远不会阻止主队列(以及UI)的方式说明信号量。在这里,我们可以在两个独立的并发运行任务之间发送十个信号,后者更新UI:
So, let's illustrate semaphores in a way where your main queue (and thus the UI) will never be blocked. Here, we can send ten signals between two separate concurrently running tasks, having the latter one update the 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");
});
诚然,这不是一个非常有用的信号量示例,但它说明了理论上你可以如何使用他们。在实践中,很少有必须使用信号量,因为对于大多数业务问题,还有其他更优雅的编码模式。如果你描述你想要做的事情,我们可以告诉你如何最好地实现它。
This is, admittedly, not a terribly useful example of semaphores, but it illustrates how theoretically you could use them. In practice, it's rare that you have to use semaphores, as for most business problems, there are other, more elegant coding patterns. If you describe what you're trying to do, we can show you how to best achieve it.
至于将非零值传递给 dispatch_semaphore_create
的示例,用于控制对某些有限资源的访问。在这个例子中,让我们假设您有100个任务要运行,但是您不希望在任何给定时间运行超过5个(例如,您正在使用网络连接(这是有限的),或者每个操作都占用了你要避免在任何给定时间运行超过五次的内存。然后你可以这样做:
As for an example with non-zero value passed to dispatch_semaphore_create
, that's used to control access to some finite resource. In this example, let's assume that you had 100 tasks to run, but you didn't want more than 5 to run at any given time (e.g. you're using network connections (which are limited), or the each operation takes up so much memory that you want to avoid having more than five running at any given time). Then you could do something like:
// 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);
});
}
});
同样,这不是信号量的一个很好的例子(在这种情况下,我通常会使用带有 maxConcurrentOperationCount
的 NSOperationQueue
,但它说明了为什么要使用非零值的示例 dispatch_source_create
。
Again, this isn't a great example of semaphores (in this case, I'd generally use an NSOperationQueue
with a maxConcurrentOperationCount
), but it illustrates an example of why you'd use a non-zero value for dispatch_source_create
.
你问了很多关于组。我认为群体与你自己的信号量无关。例如,如果要在所有任务完成时运行代码块,则可以使用组。所以这是上面示例的变体,但是当该组中的所有其他任务都完成时,使用 dispatch_group_notify
执行某些操作。
You've asked a number of questions about groups. I contend that groups are unrelated to your own semaphores. You might use a group, for example, if you want to run a block of code when all of the tasks are complete. So here is a variation of the above example, but using a dispatch_group_notify
to do something when all of the other tasks in that group are complete.
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");
});
});
这篇关于无形执行顺序(dispatch_semaphore_t,dispatch_group_async)及其与不同调度队列类型的结合使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!