如果我将dispatch_barrier_(a)同步到一个以GCD中的全局并发队列为目标的队列,会发生什么?

时间:2013-09-18 18:26:46

标签: objective-c objective-c-blocks grand-central-dispatch

我对dispatch_barrier和目标队列有疑问。 我有一个自定义串行队列和自定义并发队列,我将串行队列的目标队列设置为并发队列,然后该队列以全局并发队列为目标:

(serial queue) -> (concurrent queue) -> (global concurrent queue)

dispatch_barrier阻塞串行队列时会发生什么?它是否会阻止提交到并发队列的块的执行,或仅阻止串行队列中的执行块?或者,如果I dispatch_barrier阻塞到非全局并发队列,它是否会阻止提交到串行队列的块的执行,还是只阻止非全局并发队列中块的执行?

感谢您的关注。 :)

1 个答案:

答案 0 :(得分:5)

提交dispatch_barrier_async到串行队列与dispatch_async没什么不同,因为队列是串行的,因此没有任何读者可以拒绝,因为只有一个块可以在串行上执行一次排队。换句话说,每个块都是串行队列上的“屏障块”。

如果您dispatch_barrier_async到非全局并发队列,那么读者将被排除在该队列之外,而不是它所针对的全局队列。它仅作为其提交的队列的屏障。

如果你想进一步说服自己,可以这样考虑:所有队列最终都是针对全局并发队列之一(后台,低,默认和高优先级)。考虑到这一点,如果任何队列的dispatch_barrier*传递性地在提交的队列最终定位的全局队列上造成障碍,那么使用dispatch_barrier*使所有其他客户端无法使用它将是微不足道的。 GCD(通过向4个私有并发队列提交一个障碍块,每个队列都针对不同的优先级全局队列。)这将完全是假的。

从另一个方向来看:dispatch_barrier*特别有用因为你可以创建任意的互斥单位(即非全局并发队列)。

简而言之:您提交的队列是“保护”(或“屏障”)的单位。

编辑:如果你愿意以面值表达上述内容,你可以停止阅读,但为了更清晰一点,我编写了一个简单的例子来证明我的索赔。作为一些背景知识,这来自Apple's documentation

  

如果传递给此函数[disaptch_barrier_async]的队列是a   串行队列或全局并发队列之一,此功能   表现得像dispatch_async function

这意味着提交到串行队列的disaptch_barrier_async将不会产生外部影响,disaptch_barrier_async也不会提交到全局队列。我将证明这两个主张,而不仅仅是诉诸权威。

阻止阻止提交到专用串行队列

以下是代码:

static void FakeWork(NSString* name, NSTimeInterval duration, dispatch_group_t groupToExit);

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    dispatch_queue_t privateSerialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t privateConcurQueue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t globalConcurQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_set_target_queue(privateSerialQueue, privateConcurQueue);
    dispatch_set_target_queue(privateConcurQueue, globalConcurQueue);

    // Barrier block submitted to serial queue. Per the docs, we expect this to have no effect
    // and behave like dispatch_async. So, we expect this to run to completion in 15s.
    {
        NSString* testDesc = @"Checking for effects of barrier block on serial queue";
        dispatch_suspend(globalConcurQueue);
        dispatch_group_t group = dispatch_group_create();
        NSDate* start = [NSDate date];
        NSLog(@"%@\nStarting test run at: %@", testDesc, start);

        // We expect these to take 15s total
        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A1: 5s Job on privateSerialQueue", 5.0, group); });
        dispatch_barrier_async(privateSerialQueue, ^{ FakeWork(@"A2: 5s BARRIER Job on privateSerialQueue", 5.0, group); });
        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A3: 5s Job on privateSerialQueue", 5.0, group); });

        // So we'll make 3 15s jobs each for the privateConcurrentQueue and globalConcurrentQueue
        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B1: 15s Job on privateConcurQueue", 15.0, group); });
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B2: 15s Job on privateConcurQueue", 15.0, group); });
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B3: 15s Job on privateConcurQueue", 15.0, group); });

        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_async(globalConcurQueue, ^{ FakeWork(@"C1: 15s Job on globalConcurQueue", 15.0, group); });
        dispatch_async(globalConcurQueue, ^{ FakeWork(@"C2: 15s Job on globalConcurQueue", 15.0, group); });
        dispatch_async(globalConcurQueue, ^{ FakeWork(@"C3: 15s Job on globalConcurQueue", 15.0, group); });

        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

        NSDate* end = [NSDate date];
        NSLog(@"Test run finished at: %@ duration: %@", end, @([end timeIntervalSinceDate: start]));
    }
}

static void FakeWork(NSString* name, NSTimeInterval duration, dispatch_group_t groupToExit)
{
    NSDate* start = [NSDate date];
    NSLog(@"Starting task: %@ withDuration: %@ at: %@", name, @(duration), start);
    while (1) @autoreleasepool
    {
        NSTimeInterval t = [[NSDate date] timeIntervalSinceDate: start];
        if (t >= duration)
        {
            break;
        }
        else if ((t + 0.0005) < duration)
        {
            usleep(50);
        }
    }
    NSDate* end = [NSDate date];
    duration = [end timeIntervalSinceDate: start];
    NSLog(@"Finished task: %@ withRealDuration: %@ at: %@", name, @(duration), end);
    if (groupToExit)
    {
        dispatch_group_leave(groupToExit);
    }
}

如果dispatch_barrier_async对目标队列有任何影响,我们预计这将花费超过15秒,但这是输出:

Checking for effects of barrier block on serial queue
Starting test run at: 2013-09-19 12:16:25 +0000
Starting task: C1: 15s Job on globalConcurQueue withDuration: 15 at: 2013-09-19 12:16:25 +0000
Starting task: C2: 15s Job on globalConcurQueue withDuration: 15 at: 2013-09-19 12:16:25 +0000
Starting task: C3: 15s Job on globalConcurQueue withDuration: 15 at: 2013-09-19 12:16:25 +0000
Starting task: A1: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:16:25 +0000
Starting task: B1: 15s Job on privateConcurQueue withDuration: 15 at: 2013-09-19 12:16:25 +0000
Starting task: B2: 15s Job on privateConcurQueue withDuration: 15 at: 2013-09-19 12:16:25 +0000
Starting task: B3: 15s Job on privateConcurQueue withDuration: 15 at: 2013-09-19 12:16:25 +0000
Finished task: A1: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:16:30 +0000
Starting task: A2: 5s BARRIER Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:16:30 +0000
Finished task: A2: 5s BARRIER Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:16:35 +0000
Starting task: A3: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:16:35 +0000
Finished task: C1: 15s Job on globalConcurQueue withRealDuration: 15.00000900030136 at: 2013-09-19 12:16:40 +0000
Finished task: C2: 15s Job on globalConcurQueue withRealDuration: 15 at: 2013-09-19 12:16:40 +0000
Finished task: C3: 15s Job on globalConcurQueue withRealDuration: 15 at: 2013-09-19 12:16:40 +0000
Finished task: B1: 15s Job on privateConcurQueue withRealDuration: 15 at: 2013-09-19 12:16:40 +0000
Finished task: B2: 15s Job on privateConcurQueue withRealDuration: 15 at: 2013-09-19 12:16:40 +0000
Finished task: A3: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:16:40 +0000
Finished task: B3: 15s Job on privateConcurQueue withRealDuration: 15 at: 2013-09-19 12:16:40 +0000
Test run finished at: 2013-09-19 12:16:40 +0000 duration: 15.00732499361038

阻止阻止提交到全局并发队列

我们还要验证文档中提交到全局并发队列的障碍块没有障碍效应的要点。这是一些代码(只是与第一个例子的不同之处):

    {
        NSString* testDesc = @"Barrier block submitted to globalConcurQueue";

        dispatch_group_t group = dispatch_group_create();
        NSDate* start = [NSDate date];
        NSLog(@"%@\nStarting test run at: %@", testDesc, start);

        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A1: 5s Job on privateSerialQueue", 5.0, group); });
        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A2: 5s Job on privateSerialQueue", 5.0, group); });
        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A3: 5s Job on privateSerialQueue", 5.0, group); });

        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B1: 15s Job on privateConcurQueue", 15.0, group); });
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B2: 15s Job on privateConcurQueue", 15.0, group); });
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B3: 15s Job on privateConcurQueue", 15.0, group); });

        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_async(globalConcurQueue, ^{ FakeWork(@"C1: 15s Job on globalConcurQueue", 15.0, group); });
        dispatch_barrier_async(globalConcurQueue, ^{ FakeWork(@"C2: 15s BARRIER Job on globalConcurQueue", 15.0, group); });
        dispatch_async(globalConcurQueue, ^{ FakeWork(@"C3: 15s Job on globalConcurQueue", 15.0, group); });

        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        NSDate* end = [NSDate date];
        NSLog(@"Test run finished at: %@ duration: %@", end, @([end timeIntervalSinceDate: start]));
    }

如果提交到全局并发队列的障碍块有任何影响,我们预计这将花费超过15秒,但这是输出:

Barrier block submitted to globalConcurQueue
Starting test run at: 2013-09-19 12:33:28 +0000
Starting task: C1: 15s Job on globalConcurQueue withDuration: 15 at: 2013-09-19 12:33:28 +0000
Starting task: C2: 15s BARRIER Job on globalConcurQueue withDuration: 15 at: 2013-09-19 12:33:28 +0000
Starting task: C3: 15s Job on globalConcurQueue withDuration: 15 at: 2013-09-19 12:33:28 +0000
Starting task: B1: 15s Job on privateConcurQueue withDuration: 15 at: 2013-09-19 12:33:28 +0000
Starting task: A1: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:33:28 +0000
Starting task: B2: 15s Job on privateConcurQueue withDuration: 15 at: 2013-09-19 12:33:28 +0000
Starting task: B3: 15s Job on privateConcurQueue withDuration: 15 at: 2013-09-19 12:33:28 +0000
Finished task: A1: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:33:33 +0000
Starting task: A2: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:33:33 +0000
Finished task: A2: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:33:38 +0000
Starting task: A3: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:33:38 +0000
Finished task: C1: 15s Job on globalConcurQueue withRealDuration: 15 at: 2013-09-19 12:33:43 +0000
Finished task: C2: 15s BARRIER Job on globalConcurQueue withRealDuration: 15 at: 2013-09-19 12:33:43 +0000
Finished task: C3: 15s Job on globalConcurQueue withRealDuration: 15 at: 2013-09-19 12:33:43 +0000
Finished task: B2: 15s Job on privateConcurQueue withRealDuration: 15 at: 2013-09-19 12:33:43 +0000
Finished task: B3: 15s Job on privateConcurQueue withRealDuration: 15 at: 2013-09-19 12:33:43 +0000
Finished task: B1: 15s Job on privateConcurQueue withRealDuration: 15 at: 2013-09-19 12:33:43 +0000
Finished task: A3: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:33:43 +0000
Test run finished at: 2013-09-19 12:33:43 +0000 duration: 15.00729995965958

阻止阻止提交到专用并发队列

接下来要测试的是提交到专用并发队列的障碍块的影响。由于串行队列以专用并发队列为目标,因此我希望提交给串行队列的块可以保留提交给专用并发队列的阻塞块。事实上,情况确实如此。这是代码:

    // Barrier block submitted to private concurrent queue.
    {
        NSString* testDesc = @"Checking for effects of barrier block on private concurrent queue";
        dispatch_suspend(globalConcurQueue);
        dispatch_group_t group = dispatch_group_create();
        NSDate* start = [NSDate date];
        NSLog(@"%@\nStarting test run at: %@", testDesc, start);

        // Make 3 5s jobs on the private concurrent queue and make the middle one a barrier, which should serialize them
        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);
        dispatch_group_enter(group); dispatch_group_enter(group); dispatch_group_enter(group);

        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A1: 5s Job on privateSerialQueue", 5.0, group); });
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B1: 5s Job on privateConcurQueue", 5.0, group); });

        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A2: 5s Job on privateSerialQueue", 5.0, group); });
        dispatch_barrier_async(privateConcurQueue, ^{ FakeWork(@"B2: 5s BARRIER Job on privateConcurQueue", 5.0, group); });

        dispatch_async(privateSerialQueue, ^{ FakeWork(@"A3: 5s Job on privateSerialQueue", 5.0, group); });
        dispatch_async(privateConcurQueue, ^{ FakeWork(@"B3: 5s Job on privateConcurQueue", 5.0, group); });

        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

        NSDate* end = [NSDate date];
        NSLog(@"Test run finished at: %@ duration: %@", end, @([end timeIntervalSinceDate: start]));
    }

这是输出:

Checking for effects of barrier block on private concurrent queue
Starting test run at: 2013-09-19 12:24:17 +0000
Starting task: B1: 5s Job on privateConcurQueue withDuration: 5 at: 2013-09-19 12:24:17 +0000
Starting task: A1: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:24:17 +0000
Finished task: A1: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:24:22 +0000
Finished task: B1: 5s Job on privateConcurQueue withRealDuration: 5 at: 2013-09-19 12:24:22 +0000
Starting task: A2: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:24:22 +0000
Finished task: A2: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:24:27 +0000
Starting task: A3: 5s Job on privateSerialQueue withDuration: 5 at: 2013-09-19 12:24:27 +0000
Finished task: A3: 5s Job on privateSerialQueue withRealDuration: 5 at: 2013-09-19 12:24:32 +0000
Starting task: B2: 5s BARRIER Job on privateConcurQueue withDuration: 5 at: 2013-09-19 12:24:32 +0000
Finished task: B2: 5s BARRIER Job on privateConcurQueue withRealDuration: 5 at: 2013-09-19 12:24:37 +0000
Starting task: B3: 5s Job on privateConcurQueue withDuration: 5 at: 2013-09-19 12:24:37 +0000
Finished task: B3: 5s Job on privateConcurQueue withRealDuration: 5 at: 2013-09-19 12:24:42 +0000
Test run finished at: 2013-09-19 12:24:42 +0000 duration: 25.00404000282288

毫不奇怪,当障碍块正在执行时,它是唯一提交给正在执行的队列的块。这是因为“保护单元”是私有并发队列,其中私有串行队列是其“子单元”。我们在这里看到的奇怪的事情是,任务A3被提交到私有并发队列后提交到私有串行队列,在 B2之前执行。我不确定为什么会这样,但是基本的保护单元(即私有并发队列)没有被违反。基于此,我得出结论,即使您碰巧知道一个队列的目标是另一个队列,您也不能指望提交给两个不同队列的任务的顺序。

所以你有它。我们已经证明dispatch_barrier_async与串行和全局并发队列上的dispatch_sync相同,就像文档说的那样,只留下一个操作进行测试(dispatch_barrier_async到私有并发队列),我们已经说明了保护单元在这种情况下被保留包括提交给其他私有队列的操作。

如果在某些情况下您还不清楚,请发表评论。