异步操作并发理解

时间:2018-03-07 05:50:37

标签: ios swift grand-central-dispatch

这些队列管理您向GCD提供的任务,并按FIFO顺序执行这些任务。这保证了添加到队列中的第一个任务是队列中启动的第一个任务,添加的第二个任务将是第二个启动的任务,依此类推。 下面的代码

let anotherQueue = DispatchQueue(label: "com.gcdTest.Queue", qos: .userInteractive)
anotherQueue.async {
    anotherQueue.async{
        anotherQueue.async{
            anotherQueue.async {
                print("task 6")
                for _ in 1...300 { }
            }
        }
        print("task 3")
        for _ in 301...600 {}
    }
    anotherQueue.async{
        anotherQueue.async{
            print("task 5")
            for _ in 700...900 {}
        }
        print("task 4")
        for _ in 5000...7000 {}
    }
    print("task 1")
    for _ in 9000...10000 {}
}
anotherQueue.async {
    print("task 2")
    for _ in 1...1000 {}
}

产生输出

task 1
task 2
task 3
task 4
task 5
task 6

但是当我们在Concurrent中运行相同的代码时,会产生不可预测的输出。 例如: - 将第一行代码更改为以下行

let anotherQueue = DispatchQueue(label: "com.gcdTest.Queue", qos: .userInteractive, attributes: .concurrent)

输出

task 3
task 2
task 1
task 4
task 5
task 6

根据定义,它说明 并发队列中的任务保证按照添加的顺序开始......这就是所有你保证的!

因此,期望串行队列产生类似的输出(默认情况下)。 (task1,task2,task3,task4,task5,task6) 请任何人帮助我,我出错了。

1 个答案:

答案 0 :(得分:1)

从底线开始,GCD将始终按照调度到该队列的顺序启动队列中的任务。在串行队列的情况下,这意味着它们将按顺序依次运行,并且这种行为很容易被观察到。

在并发队列的情况下,虽然它将以排队顺序启动任务,但对于连续快速调度的任务,它们也可能连续快速启动,并且彼此同时运行。简而言之,它们可能几乎在同一时间开始运行,因此您无法保证首先会遇到其各自的print声明。仅仅因为并发队列在几毫秒之后启动了一个任务,这无法保证这两个任务遇到各自print语句的顺序。

简而言之,不是print语句序列的确定性行为,而是一场具有非确定性行为的简单竞赛。

顺便说一下,虽然很明显你的例子在并发队列中使用时引入了比赛,但应该注意的是,由于你的嵌套调度语句,你的串行队列也会有竞争条件。看起来行为序列在串行队列上完全可以预测,但事实并非如此。

让我们考虑一下你的例子的简化版本。我假设我们将从主线程开始:

queue.async {
    queue.async {
        print("task 3")
    }
    print("task 1")
}
queue.async {
    print("task 2")
}

显然,任务1将首先添加到队列中,如果该队列是空闲的,它将立即在该后台线程上启动,同时主线程继续进行。但是当主线程上的代码接近任务2的调度时,任务1将启动并将继续调度任务3.在调度任务2和任务3之间存在经典竞争。

现在,在实践中,您将看到在任务3之前调度任务2,但引入非确定性行为并不需要太多延迟。例如,在我的计算机上,如果在调度任务2之前,Thread.sleep(forTimeInterval: 0.00005)表现出非确定性行为。但即使没有延迟(或一定次数的迭代的for循环),该行为在技术上也是不确定的。

但是我们可以创建简单的示例来消除上面示例中隐含的种族,但仍然说明了您最初询问的串行和并发队列行为之间的区别:

for i in 0 ..< 10 {
    queue.async { [i] in
        print(i)
    }
}

保证在串行队列上按顺序打印,但不一定在并发队列上打印。