DispatchSemaphore 是否等待特定的线程对象?

Does DispatchSemaphore wait for specific thread objects?

我今天实现了一个信号量,它提出了一些关于信号量、线程和队列的问题。我的以下符号准确吗?

let semaphore = DispatchSemaphore(value: 1)
let serialQueue = DispatchQueue(label: "serial")

someAsyncMethod {

    serialQueue.async {

        // When someAsyncMethod returns, this queue, behind the scenes,
        // creates a Thread/NSThread object and approaches the
        // semaphore and it is this thread object that decrements
        // the semaphore counter.

        // The next time someAsyncMethod returns, a new thread object
        // is created behind the scenes (by the queue) and this thread
        // object is made to wait until something (anything) signals
        // it is done.

        semaphore.wait()

        // do work...
        someMethod()

    }

}

func someMethod() {

    // do work...

    // This task may ultimately be on a different thread than
    // where it started despite being in the same queue
    // (since not every task is guaranteed to run on the same thread)
    // but it doesn't matter, any object can signal the semaphore.

    semaphore.signal()

}
  1. 信号量是否响应特定线程 objects/instances?
  2. 是否每次someAsyncMethodreturns创建一个新线程object/instance并入队?

信号量不是特定于线程的。重点是在线程之间进行协调,因此它们必须可以被多个线程使用。

在这种特定情况下,不需要信号量(如果这些是唯一的用途),因为您使用的是串行队列。根据定义,串行队列一次只允许队列中的一个任务运行,按照先进先出的顺序。也就是说,第二个任务不需要等待信号量以避免 运行 与第一个任务同时进行,因为它甚至不允许在第一个任务完成之前启动。

当您将任务异步放入队列时,调用代码可以立即继续。任务更像是被放入数据结构(队列)中的数据对象。 Grand Central Dispatch 使用线程池从队列中弹出任务并执行它。是否必须创建新线程取决于池中是否已经有足够的空闲线程。

基础知识是这样的:

队列 是一种用于管理任务集合的工具。

每个任务都在一个线程上执行。

信号量是协调任务的方式,可能在不同的线程上运行。

是否每个任务都创建一个新线程,或者只有一些任务创建,或者所有任务 运行 在同一个线程上 完全取决于 Grand Central Dispatch。 GCD 将您从担心线程的负担中解脱出来。线程将根据您系统的功能、资源和负载,按照 GCD 认为合适的方式创建、销毁或重用。

这只是一种礼貌的说法 "stop thinking about threads." 假设每个任务将 运行 在自己的时间在自己的线程上(除了像 Main 这样非常特殊的队列) .任务实际执行的线程是新线程还是重用线程,以及该线程在任务 运行s 后被销毁还是被另一个任务重用是幕后人员的领域。

如果你需要协调一个资源池或等待一组任务完成,那就是你使用信号量的时候and/or任何基于队列的工具,如串行队列、调度组、屏障,等等。

希望对您有所帮助。

让我们一次解决这些问题:

someAsyncMethod {

    serialQueue.async {

        // When someAsyncMethod returns, this queue, behind the scenes,
        // creates a Thread/NSThread object and approaches the
        // semaphore and it is this thread object that decrements
        // the semaphore counter.

        // The next time someAsyncMethod returns, a new thread object
        // is created behind the scenes (by the queue) and this thread
        // object is made to wait until something (anything) signals
        // it is done.

        semaphore.wait()

        // do work...
        someMethod()
    }
}

首先,一个小细节,但 async 不会“创建”线程。 GCD 有一个工作线程池,async 只是抓取其中一个空闲的工作线程。 (这就是为什么 GCD 是高性能的,因为它一直避免 creating/destroying 线程,这是一项代价高昂的工作。它利用了它的工作线程池。)

您第二次在同一个串行队列上调用 async,下次您可能会得到相同的线程。或者您可能会从池中获得不同的工作线程。无论哪种方式,您都无法保证。您唯一的保证是队列是串行的(因为您定义了 就这样)。

但是,是的,如果信号量以计数 1 开始,第一次它只会递减计数器并继续。而且,是的,如果在你到达那一秒时还没有发出信号 wait,是的,它会等待 signal.

但是将这个非零分派信号量与串行队列结合使用的想法似乎非常值得怀疑。通常您使用串行队列来协调不同的任务,或者在极少数情况下使用信号量,但几乎不会同时使用这两种任务。一般来说,信号量的存在是令人担忧的,因为几乎总是有更好的解决方案可用。

然后你有:

func someMethod() {

    // do work...

    // This task may ultimately be on a different thread than
    // where it started despite being in the same queue
    // (since not every task is guaranteed to run on the same thread)
    // but it doesn't matter, any object can signal the semaphore.

    semaphore.signal()

}

这段代码,因为它是从前一个代码块的 serialQueue.async 内部调用的,所以绝对会 运行 在您调用 someMethod.[=32 的同一线程上=]

所以,因此,这段代码不太有意义。您永远不会从同一个线程 signal 调用同一个线程上的 wait。信号量的全部意义在于,一个线程可以 wait 获取来自 不同 线程的信号。

例如例如,如果 someMethod 正在做一些等同于:

的事情,这可能是有意义的
func someMethod() {
    someOtherQueue.async {
        // do work...

        // Because this is dispatched to a different queue and we know is
        // now running on a different thread, now the semaphore has some 
        // utility, because it only works if it’s a different thread than
        // where we’ll later call `wait`...

        semaphore.signal()
    }
}

现在,所有这些都引出了关于串行队列与此信号量的用途的问题。恐怕在尝试将其从代码库的不相关细节中抽象出来时,您已经将其抽象得太抽象了。因此,如果不了解这段代码试图解决的更广泛的问题,就很难就更好的模式提出建议。

但是,无意冒犯,几乎可以肯定会有更好的方法。我们不能只是根据目前提供的信息提出建议。


您最后提出了两个问题:

  1. Do semaphores respond to specific thread objects/instances?

没有。一个线程向另一个不同的线程发出信号。但除此之外没有其他限制。

  1. Is a new thread object/instance created every time someAsyncMethod returns and enters the queue?

正如我希望我在上面说清楚的那样,使用 async 分派到串行队列的闭包可能会在同一个工作线程上结束,也可能会在另一个工作线程上结束。

但这不是这里真正关心的问题。真正关心的是 waitsignal 调用是从不同的线程进行的,而不是后续的 wait 调用是否是。