无形执行顺序(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

查看:215
本文介绍了无形执行顺序(dispatch_semaphore_t,dispatch_group_async)及其与不同调度队列类型的结合使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是花了一些时间在晚上玩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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆