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

Ios DispatchSemaphore是否等待特定的线程对象?,ios,swift,grand-central-dispatch,semaphore,Ios,Swift,Grand Central Dispatch,Semaphore,今天我实现了一个信号量,它提出了一些关于信号量、线程和队列的问题。我下面的记号准确吗 let semaphore = DispatchSemaphore(value: 1) let serialQueue = DispatchQueue(label: "serial") someAsyncMethod { serialQueue.async { // When someAsyncMethod returns, this queue, behind the scene

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

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()

}
信号量是否响应特定的线程对象/实例? 是否每次someAsyncMethod返回并进入队列时都会创建一个新的线程对象/实例?
信号量不是特定于线程的。整个要点是在线程之间进行协调,因此它们必须可由多个线程使用

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

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

基本情况如下:

队列是管理任务集合的工具

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

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

无论是每个任务创建一个新线程,还是只有一些任务创建一个新线程,或者可能所有任务都运行在同一个线程上,都完全取决于Grand Central Dispatch。GCD减轻了您担心线程的负担。根据系统的能力、资源和负载,GCD认为合适时,将创建、销毁或重用线程

这只是一种礼貌的说法,表示停止思考线程。假设每个任务将在自己的时间内在自己的线程上运行,但Main之类的非常特殊的队列除外。任务实际执行的线程是新线程还是重用的线程,以及该线程是在任务运行后被销毁还是被重新用于另一个任务,这是幕后工作人员的职责

如果您需要协调资源池或等待一组任务完成,则需要使用信号量和/或任何基于队列的工具,如串行队列、调度组、屏障等


我希望这会有所帮助。

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

首先是一个次要的细节,但异步不“创建”线程。GCD有一个工作线程池,而async只获取其中一个空闲工作线程。这就是为什么GCD是高性能的,因为它避免了一直创建/销毁线程,这是一项代价高昂的工作。它利用其工作线程池

第二次在同一串行队列上调用async时,下次可能会得到相同的线程。或者,您可能会从池中获得不同的工作线程。你没有任何保证。您唯一的保证是队列是串行的,因为您定义了 就其本身而言

但是,是的,如果信号量以计数1开始,第一次它将减少计数器并继续。是的,如果在你到达第二次等待时,信号灯还没有发出信号,它会等待信号

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

你当时有:

这段代码,因为它是从上一个代码块的serialQueue.async内部调用的,绝对仍将在调用someMethod的同一线程上运行

因此,这段代码没有什么意义。您永远不会从同一线程发出信号,该线程将在同一线程上调用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()
    }
}
现在,所有这些都引出了一个问题,即串行队列与此信号量的用途。我担心,在试图将其从代码库的无关细节中抽象出来时,您已经将其抽象化了一点。因此,如果不理解这段代码试图解决的更广泛的问题,就很难为您提供更好的模式建议

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

你最后提出了两个问题:

信号量是否响应特定的threa d对象/实例? 没有。这是一个线程向另一个不同的线程发送信号。但除此之外,没有其他约束

是否每次someAsyncMethod返回并进入队列时都会创建一个新的线程对象/实例? 正如我希望在上面已经阐明的那样,使用async调度到串行队列的闭包可能会在同一个工作线程上结束,也可能在另一个工作线程上结束


但这不是真正的问题。真正令人担心的是等待和信号调用是从不同的线程发出的,而不是后续的等待调用。

我知道在这个伪代码中不需要信号量;为了说明我的问题,我特意简化了这个例子。我知道信号量不是特定于线程的,但这不是我的问题。我的问题是关于队列内部线程的创建以及信号量如何区分请求者。我读过的每一篇关于这个主题的文章都会在线程和队列不能互换时互换使用它们。我认为人们在很大程度上误解了这一点,或者忽略了这一点,这就引出了我的问题。我不太清楚你所说的区分请求者是什么意思,但是,好吧,他们没有。这个例子并不简单,它意味着信号量永远不会有争议。没有任何东西会阻止该信号量,因此该信号量的行为是没有意义的。不是真的,因为您不知道这些方法中执行了什么样的工作以及如何调用它们。如果该方法中只有一个额外的异步任务,并且该方法被快速连续调用,则会出现争用。如果信号量的唯一用途是所示的,则不会出现争用。如果还有其他用途,那么这个例子只是误导。无论如何,你问的问题我都没有解释清楚。例如,您从未解释过如何区分请求者。我试图解决的问题是:一个数据库方法快速连续地返回结果,我将此返回放入一个串行队列中,这很好。然而,这种解析这些结果的方法演变成了更复杂的东西,在调用complete之前,我必须在该方法的循环中发出异步请求。因此,我在每次迭代中使用了一个调度组enter,在异步返回成功或失败时使用leave。我遇到的问题是,当调用dispatch.notifyqueue:execute:时,我必须选择一个队列来完成作业……并且我选择了串行队列。问题是,当它到达这一点时,数据库已经返回了一组新的结果,并且是串行队列中的下一个队列。我假设notify在其他任何东西进入队列之前是下一个队列,但事实并非如此。这导致返回方法与自身交错。我本想创建一个额外的串行队列并嵌套它们,但最终我使用了一个全局并发队列来处理带有信号量的方法,它按预期工作。@hgale-如果您想通知异步任务循环已完成,我们可能不会在数据库队列上通知,而是在主队列上通知,这样您就可以在成功完成后更新UI。视频中大约8:18的内容提供了思考如何构建复杂应用程序的好方法。您是否需要所有这些队列尚不清楚,但只是一个想法。但我会尽力去除信号量。阻止线程的效率很低,而且会带来死锁风险。但是如果我不想通知主线程,因为后面还有另一个异步任务,该怎么办?信号量在告诉数据库返回等待直到最后一次传递完成时做了如此有效的工作,但是它会迂回通过其他队列。这种模式真的需要避免吗?首先,是的,信号量模式真的需要避免。如果我们需要管理异步任务之间的依赖关系,我们可以使用自定义的异步操作对象子类。其次,您真的需要让后续数据库更新等待前一批的异步请求吗?这就是为什么我将该链接与单独子系统的单独队列共享。如果您确实需要第二批数据库请求来等待第一批异步请求,请执行您必须执行的操作,但这听起来并不正确。
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()

}
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()
    }
}