并发队列中的DispatchQueue sync和sync barrier有什么区别?

27

我正在使用DispatchQueue的屏障操作,并注意到有两种方式:async(flags: .barrier)sync(flags: .barrier)

我理解了异步屏障的用法,但对同步屏障感到困惑。

我不确定这也能完成我想要做的任务。

DispatchQueue.global().sync {

}

那么同步屏障有什么用处?它们为什么被使用?以及它们在这方面的不同之处。

Translated text:

那么同步屏障有什么用处?它们为什么被使用?以及它们在这方面的不同之处。

DispatchQueue.global().sync(flags: .barrier) {

}
3个回答

59

这里需要考虑两个不同的问题:

同步/异步决定了在提交队列上程序的执行流程:使用 sync() 会导致在任务完成前,提交队列上的执行被阻塞;相反地,使用 async() 则不会阻塞。

然而,使用标志.barrier会影响块在它们被提交到的队列上的执行方式(显然,这只有在并发队列上才有区别):

使用此标志提交的块将充当障碍:所有在障碍之前提交的其他块都将完成,然后才会执行障碍块。在障碍之后提交的所有块将等待障碍完成后才会开始执行。

注意:障碍标志对全局队列没有影响。为了能够使用障碍块,您必须创建自己的并发队列。(感谢 Rob 明确指出这一点!)


29
Lutz says (+1) 中,同步/异步和屏障是完全不同的问题。同步/异步决定了调用线程的行为(即它是否等待)。屏障则决定了被调度到队列上的块是否可以与任何其他已调度块并发运行。
需要注意的是,屏障不适用于全局队列;它们只影响您创建的私有并发队列。正如关于屏障所述的文档

您指定的队列应该是您自己创建的并发队列...如果您传递给此函数的队列是串行队列或全局并发队列之一,则此函数的行为类似于未使用屏障调度。


3
为什么屏障不影响全局并发队列?它们与受到屏障影响的普通私有并发队列有何不同?你有什么想法? - Hudi Ilfeld
10
全局队列是共享的。你不是唯一一个可能使用这些队列的人。你的应用程序中的其他子系统也可能在使用它们。操作系统也可能在使用它们。而障碍是一种阻塞操作,如果它们开始阻塞不相关的系统,可能会产生严重影响。对我来说,GCD很明智地防止了一个子系统中的一段代码阻塞所有完全不相关的子系统。 - Rob

24

在一个障碍同步任务被指定之前分配的任务(同步/异步)将首先完成。然后障碍同步任务将独占地执行,由于它是一个同步任务,在它完成之前不会启动任何其他任务。

具有障碍/非障碍同步和异步的示例并发队列。

let dispatchQueueA = DispatchQueue(label: "A", attributes: .concurrent)

dispatchQueueA.async { // Task1
    for index in 0 ..< 5 {
        sleep(2)
        print("Task 1 - async \(index)")
    }
}

dispatchQueueA.sync { // Task2
    for index in 0 ..< 5 {
        sleep(1)
        print("Task 2 - sync without barrier \(index)")
    }
}

dispatchQueueA.sync(flags: .barrier) { // Task3
    // the tasks(sync, async) assigned before this block will be completed first
    // then this task will execute and as it is sync task no other task will start until it finishes
    for index in 0 ..< 5 {
        sleep(1)
        print("Task 3 - sync with barrier \(index)")
    }
}

dispatchQueueA.async { // Task4
    for index in 5 ..< 10 {
        sleep(1)
        print("Task 4 - async \(index)")
    }
}

dispatchQueueA.sync { // Task5
    for index in 5 ..< 10 {
        sleep(1)
        print("Task 5 - sync without barrier \(index)")
    }
}

以上代码的输出-在完成Task1和Task2后执行Task3(同步屏障)。完成Task3(同步屏障)后,已启动下一个指定任务(Task4、Task5)。

Task 2 - sync without barrier 0
Task 1 - async 0
Task 2 - sync without barrier 1
Task 2 - sync without barrier 2
Task 2 - sync without barrier 3
Task 1 - async 1
Task 2 - sync without barrier 4
Task 1 - async 2
Task 1 - async 3
Task 1 - async 4
Task 3 - sync with barrier 0
Task 3 - sync with barrier 1
Task 3 - sync with barrier 2
Task 3 - sync with barrier 3
Task 3 - sync with barrier 4
Task 5 - sync without barrier 5
Task 4 - async 5
Task 5 - sync without barrier 6
Task 4 - async 6
Task 5 - sync without barrier 7
Task 4 - async 7
Task 5 - sync without barrier 8
Task 4 - async 8
Task 5 - sync without barrier 9
Task 4 - async 9

如果您将dispatchQueueA替换为全局后台队列,即DispatchQueue.global(qos: .background).async(flags: .barrier){ },则屏障的工作效果不如预期。这种行为有什么原因吗? - G.Abhisek
3
@G.Abhisek,你不能在全局队列中使用.barrier标志。你需要创建自己的队列。 - emin deniz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接