swift – 一次调用DispatchSemaphore的wait()这几次是否安全?

我有三个名为queueA,queueB,queueC的调度线程.
现在我希望在queueB和queueC完成之后执行queueA.
所以我尝试通过DispatchSemaphore实现它.
我的问题是:
一次在一个线程中调用wait()两次以使信号量2成为安全吗?

self.semaphore.wait()  // +1
 self.semaphore.wait()  // +1

以下是整个测试代码:

class GCDLockTest {
    let semaphore = DispatchSemaphore(value: 0) 

    func test() {

        let queueA = DispatchQueue(label: "Q1")
        let queueB = DispatchQueue(label: "Q2")
        let queueC = DispatchQueue(label: "Q3")
        queueA.async {
            self.semaphore.wait()  // +1
            self.semaphore.wait()  // +1
            print("QueueA gonna sleep")
            sleep(3)
            print("QueueA woke up")                
        }
        queueB.async {
            self.semaphore.signal()  // -1
            print("QueueB gonna sleep")
            sleep(3)
            print("QueueB woke up")

        }
        queueC.async {
            self.semaphore.signal()  // -1
            print("QueueC gonna sleep")
            sleep(3)
            print("QueueC wake up")

        }
    }
}

解决方法

首先,在一个稍微迂腐的音符上,信号增加信号量并等待减少它(除非它是零,在这种情况下它等待).

Is this safe to call wait() two times in a thread at one time to make the semaphore 2 [sic]?

信号量动作保证是线程安全的,如果没有它就没有意义,所以你正在做的事情会很好.你确实可以调用等待两次来概念性地获取资源两次.

但是,您有一个阻塞的后台线程.这是一件坏事,因为用于执行调度队列上的块的线程在需要时不会创建,它们是根据基于各种事物(如处理器内核数量)调整大小的池分配的.队列A上的块将占用一个线程,直到队列B和队列C线程都发信号通知信号量.

当您输入函数test()且线程池中只剩下一个线程时,会出现最坏的情况.如果队列A上的块在其他两个块中的任何一个块之前抓取它,则会有一个死锁,因为A将等待信号量,B和C将等待A完成,以便它们可以有一个线程.

最好不要启动A,直到其他两个线程准备好开始它.这可以通过在正确的时间在主线程上执行块来完成.像这样的东西:

class GCDLockTest {
    var cFinished = false
    var bFinished = false 

    func test() {

        let queueA = DispatchQueue(label: "Q1")
        let queueB = DispatchQueue(label: "Q2")
        let queueC = DispatchQueue(label: "Q3")
        queueB.async {
             DispatchQueue.main.async
             {
                 bFinished = true
                 if cFinished
                 {
                     queueA.async {
                         print("QueueA gonna sleep")
                         sleep(3)
                         print("QueueA woke up")                
                     }
                 }
             }
            print("QueueB gonna sleep")
            sleep(3)
            print("QueueB woke up")

        }
        queueC.async {
             DispatchQueue.main.async
             {
                 cFinished = true
                 if bFinished
                 {
                     queueA.async {
                         print("QueueA gonna sleep")
                         sleep(3)
                         print("QueueA woke up")                
                     }
                 }
             }
            print("QueueC gonna sleep")
            sleep(3)
            print("QueueC wake up")

        }
    }
}

在上面,您不需要任何信号量或其他同步,因为隐含的是所有同步工作都是在串行的主队列上完成的.即,启动A的两个块永远不能同时运行.

这是一种方法,但Apple提供dispatch groups确切的问题.使用调度组,您可以将B和C添加到组中,并让它们在准备好启动A时告诉组.

class GCDLockTest {

    func test() {

        let group = DispatchGroup()

        let queueA = DispatchQueue(label: "Q1")
        let queueB = DispatchQueue(label: "Q2")
        let queueC = DispatchQueue(label: "Q3")
        group.enter()
        queueB.async {
            group.leave()
            print("QueueB gonna sleep")
            sleep(3)
            print("QueueB woke up")

        }
        group.enter()
        queueC.async {
            group.leave()
            print("QueueC gonna sleep")
            sleep(3)
            print("QueueC wake up")

        }
        group.notify(queue: queueA) {
            print("QueueA gonna sleep")
            sleep(3)
            print("QueueA woke up")                
    }
}

在开始B和C中的每一个之前,输入组.然后在启动B和C之后,我们在组中放置一个通知块,这样当它们都离开组时,A的块在右侧队列中启动.

另见https://developer.apple.com/documentation/dispatch/dispatchgroup

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...