我假设我知道如何使用DispatchGroup,为了理解这个问题,我try 过:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        performUsingGroup()
    }

    func performUsingGroup() {
        let dq1 = DispatchQueue.global(qos: .userInitiated)
        let dq2 = DispatchQueue.global(qos: .userInitiated)

        let group = DispatchGroup()

        group.enter()
        dq1.async {
            for i in 1...3 {
                print("\(#function) DispatchQueue 1: \(i)")
            }
            group.leave()
        }

        group.wait()

        dq2.async {
            for i in 1...3 {
                print("\(#function) DispatchQueue 2: \(i)")
            }
        }

        group.notify(queue: DispatchQueue.main) {
            print("done by group")
        }
    }
}

正如预期的那样,结果是:

performUsingGroup() DispatchQueue 1: 1
performUsingGroup() DispatchQueue 1: 2
performUsingGroup() DispatchQueue 1: 3
performUsingGroup() DispatchQueue 2: 1
performUsingGroup() DispatchQueue 2: 2
performUsingGroup() DispatchQueue 2: 3
done by group

为了使用信号量,我实现了:

func performUsingSemaphore() {
    let dq1 = DispatchQueue.global(qos: .userInitiated)
    let dq2 = DispatchQueue.global(qos: .userInitiated)

    let semaphore = DispatchSemaphore(value: 1)

    dq1.async {
        semaphore.wait()
        for i in 1...3 {
            print("\(#function) DispatchQueue 1: \(i)")
        }
        semaphore.signal()
    }

    dq2.async {
        semaphore.wait()
        for i in 1...3 {
            print("\(#function) DispatchQueue 2: \(i)")
        }
        semaphore.signal()
    }
}

并在viewDidLoad方法中调用它.结果是:

performUsingSemaphore() DispatchQueue 1: 1
performUsingSemaphore() DispatchQueue 1: 2
performUsingSemaphore() DispatchQueue 1: 3
performUsingSemaphore() DispatchQueue 2: 1
performUsingSemaphore() DispatchQueue 2: 2
performUsingSemaphore() DispatchQueue 2: 3

从概念上讲,DispachGroup和Semaphore都有相同的用途(除非我误解了什么).

老实说,我不熟悉:什么时候使用信号量,尤其是在使用DispachGroup时——可能——处理这个问题.

我错过了什么?

推荐答案

从概念上讲,DispatchGroup和信号量都有相同的用途(除非我误解了什么).

上述说法并不完全正确.您可以使用信号量做与调度组相同的事情,但它更通用.

当你有一大堆你想做的事情可以同时发生,但你需要等待它们全部完成,然后再做其他事情时,就可以使用调度组.

信号量can可用于上述目的,但它们是通用同步对象,也可用于许多其他目的.信号量的概念不仅限于苹果,而且可以在许多操作系统中找到.

通常,信号量有一个非负整数的值和两个操作:

  • wait如果该值不是零,则将其递减,否则阻塞,直到有东西向信号量发出信号.

  • signal如果有线程在等待,则取消阻止其中一个线程,否则增加该值.

不用说,这两种操作都必须是线程安全的.在过go ,当你只有一个CPU时,你只需要在操作值和等待线程队列的同时禁用中断.如今,由于多个CPU核和片上缓存等原因,它变得更加复杂.

在任何情况下都可以使用一个信号量,在这种情况下,最多N个线程可以同时访问一个资源.将信号量的初始值设置为N,然后等待它的前N个线程不会被阻塞,但下一个线程必须等待,直到前N个线程中的一个发出信号量.最简单的情况是N=1.在这种情况下,信号量的行为类似于互斥锁.

信号量可用于模拟调度组.在0处启动sempahore,启动所有任务——跟踪已启动的任务数,并等待信号量的次数.每个任务完成时都必须向信号量发送信号.

然而,也存在一些问题.例如,您需要一个单独的计数来知道等待的次数.如果希望在开始等待后向组中添加更多任务,则只能在受互斥保护的块中更新计数,这可能会导致死锁问题.此外,我认为信号量的分派实现可能容易受到priority inversion的攻击.当高优先级线程等待低优先级线程占用的资源时,就会发生优先级反转.高优先级线程被阻塞,直到低优先级线程释放资源.如果有一个中等优先级的线程在运行,这可能永远不会发生.

你几乎可以用一个信号量做任何事情,其他更高级别的同步抽象也可以做到,但是正确地做这件事通常是一件棘手的事情.更高级别的抽象(希望)是经过仔细编写的,如果可能的话,您应该优先使用它们,而不是使用带有信号量的"滚动您自己的"实现.

Swift相关问答推荐

计算Vision OS相机与Vision OS中3D模型之间的距离

变量捕获:变量在函数闭包中的行为

更改正在进行的异步任务的完成(SWIFT并发)(&q;)?

带有文本输入自动大小写修饰符的SwiftUI Textfield问题

是否在核心数据中使用Uint?

了解SWIFT中的命名VS位置函数调用

为什么要在SWIFT RandomAccessCollection协议中重新定义元素类型?

如何在SWIFT Memory中处理对VAR数组的更新(对COW感到困惑)

从任务中打印

swift:只要框架名称中有一个带有框架名称的公共实体,就指定框架中公共 struct 的路径

Swift ui 转换无法按预期工作

TimeZone 背后的目的

用逻辑运算符保护让

SwiftUI 分段 Select 器的问题

展开 List 中的 HStack 以端到端但不是从上到下,因此看起来项目之间有更宽的空间

为什么swiftui中的导航视图栏那么大?

Swift - 获取本地日期和时间

subscribeOn 和 observeOn 的顺序重要吗?

在 Swift 中将计时器标签格式化为小时:分钟:秒

swift中相同的数据类型多变量声明