从多个BackgroundThreads到MainThread的调用可以同时发生吗?

时间:2019-03-20 10:18:27

标签: ios swift

也许有很多更好的方法可以用一种完全不同的方法来解决这个问题,但是我很幽默。我想知道这种确切情况。

在这里,我有两个功能,doSomethingdoSomethingElse。它们是从doEverything()调用的,并且都返回会触发异步的completeBlock。

目标是在这两个异步调用均完成后立即调用everythingDone()。如我所说,可能有更好的解决方法,但我想知道这种精确的逻辑会发生什么。

在两个完成中,我检查两个完成是否都已完成,然后调用everythingDone

func doSomething(completion:((Int)->())?){
    DispatchQueue.global(qos: .background).async {
        completion?(123)
    }
}
func doSomethingElse(completion:((String)->())?){
    DispatchQueue.global(qos: .background).async {
        completion?("Test")
    }
}

func doEverything(){
    var values:[Any] = []
    var somethingDone:Bool = false
    var somethingElseDone:Bool = false

    doSomething { (value) in
        DispatchQueue.main.async {
            values.append(value)
            somethingDone = true
            if somethingDone && somethingElseDone{
                self.everythingDone(values: values)
            }
        }
    }
    doSomethingElse { (value) in
        DispatchQueue.main.async {
            values.append(value)
            somethingElseDone = true
            if somethingDone && somethingElseDone{
                self.everythingDone(values: values)
            }
        }
    }
}

func everythingDone(values:[Any]){
    print("Everything done: ", values)
}

everythingDone会发生两次吗?事件的顺序是否有可能导致此情况:

  1. DoSomething-completion集somethingDone=true
  2. DoSomethingElse完成集somethingElseDone = true
  3. DoSomething-completion检查两者是否为真
  4. DoSomethingElse-completion检查两者是否均为真。

这会发生吗? main.asyc调用可以“交织”吗?

1 个答案:

答案 0 :(得分:5)

简短答案
没有。 everythingDone不能被调用两次。

详细答案
主要的 queue DispatchQueue.main)是 serial queue ,这意味着任务将一个接一个地完成,第二个DispatchQueue.main.async关闭将完成等待,直到第一个完成任务。

一些示范:
想象以下代码:

    DispatchQueue.global(qos: .background).async {
        DispatchQueue.main.async {
            //Closure A
            for i in 0..<10 {
                print("a\(i)")
            }
        }
    }


    DispatchQueue.global(qos: .background).async {
        DispatchQueue.main.async {
            //Closure B
            for i in 0..<10 {
                print("b\(i)")
            }
        }
    }

如果运行此代码,则在控制台中将看到以下结果:

  

a0   a1   a2   a3   a4   a5   a6   a7   a8   a9   00   11   b2   b3   44   55   66   7   88   b9

如您所见,首先,它运行并完成Close A内的代码,只有在它开始并完成Closure B内的代码之后。

但是,如果我们稍微修改一下代码(通过将闭包直接移到全局队列

    DispatchQueue.global(qos: .background).async {
        //Closure A
        for i in 0..<10 {
            print("a\(i)")
        }
    }


    DispatchQueue.global(qos: .background).async {   
        //Closure B         
        for i in 0..<10 {
            print("b\(i)")
        }
    }

结果如下:

  

b0   00   11   b2   b3   44   55   a1   66   a2   7   a3   88   a4   a5   a6   a7   a8   a9   b9

在这里您可以看到顺序已中断,甚至是不可预测的,并且每次执行时都可能更改。 这是因为DispatchQueue.global(qos: .background)并发队列,因此任务将同时执行,并且将在意外的时间表上完成。

因此,一旦您的封包进入串行队列(在您的情况下为主队列),答案为,否则,答案为