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

Pit*_*ont 0 objective-c grand-central-dispatch objective-c-blocks

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

(serial queue) -> (concurrent queue) -> (global concurrent queue)
Run Code Online (Sandbox Code Playgroud)

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

感谢您的关注.:)

ipm*_*mcc 5

提交dispatch_barrier_async到串行队列没有什么不同,dispatch_async因为队列是串行的,所以没有任何读取器可以阻止,因为一次只能有一个块在串行队列上执行.换句话说,每个块都是串行队列上的"屏障块".

如果您dispatch_barrier_async访问非全局并发队列,那么读者将不在该队列中,而不是它所针对的全局队列.它仅作为其提交的队列的屏障.

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

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

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

编辑:如果你愿意以表面价值取得上述内容,你可以停止阅读,但为了更加清晰,我编写了一个简单的例子来证明我的主张.作为一些背景,这来自Apple的文档:

如果传递给此函数[ disaptch_barrier_async]的队列是串行队列或全局并发队列之一,则此函数的行为类似于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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果a 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
Run Code Online (Sandbox Code Playgroud)

阻挡块提交到全局并发队列

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

    {
        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]));
    }
Run Code Online (Sandbox Code Playgroud)

如果提交到全局并发队列的障碍块有任何影响,我们预计这将花费超过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
Run Code Online (Sandbox Code Playgroud)

屏障块提交到专用并发队列

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

    // 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]));
    }
Run Code Online (Sandbox Code Playgroud)

这是输出:

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
Run Code Online (Sandbox Code Playgroud)

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

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

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