首页 > 解决方案 > 从多个 BackgroundThreads 到 MainThread 的调用可以同时发生吗?

问题描述

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

这里我有两个函数,doSomethingdoSomethingElse. 它们被调用 from doEverything(),并且都返回一个将触发异步的 completionBlock。

目标是在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-完成集somethingDone=true
  2. DoSomethingElse-完成集somethingElseDone = true
  3. DoSomething-completion 检查两者是否为真,它们是
  4. DoSomethingElse-completion 检查两者是否为真,它们是。

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

标签: iosswift

解决方案


简短的回答
不。everythingDone不能调用两次 。

长答案
队列DispatchQueue.main)是一个串行队列,这意味着任务将一个接一个完成,第二个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 b0 b1 b2 b3 b4 b5 b6 b7 b8 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 a0 b1 b2 b3 b4 b5 a1 b6 a2 b7 a3 b8 a4 a5 a6 a7 a8 a9 b9

在这里你可以看到顺序是被破坏的,甚至是不可预测的,每次执行都可能发生变化。这是因为DispatchQueue.global(qos: .background)并发队列,所以任务将同时执行,并且会在意想不到的时间表上完成。

因此,一旦您的闭包在串行队列中(在您的情况下是主队列),那么答案是NO,否则,答案是YES


推荐阅读