我有一个类,其中包含两个方法,根据Jon Hoffman的Mastering Swift中的示例。课程如下:
class DoCalculation {
func doCalc() {
var x = 100
var y = x * x
_ = y/x
}
func performCalculation(_ iterations: Int, tag: String) {
let start = CFAbsoluteTimeGetCurrent()
for _ in 0..<iterations {
self.doCalc()
}
let end = CFAbsoluteTimeGetCurrent()
print("time for \(tag): \(end - start)")
}
}
现在,在单视图模板的ViewController的viewDidLoad()中,我创建了上面一个类的实例,然后创建了一个并发队列。然后我将执行performCalculation(:tag :)方法的块添加到队列中。
cqueue.async {
print("Starting async1")
calculation.performCalculation(10000000, tag: "async1")
}
cqueue.async {
print("Starting async2")
calculation.performCalculation(1000, tag: "async2")
}
cqueue.async {
print("Starting async3")
calculation.performCalculation(100000, tag: "async3")
}
每次我在模拟器上运行应用程序时,都会随机输出start语句。我得到的示例输出如下:
Example 1:
Starting async1
Starting async3
Starting async2
time for async2: 4.1961669921875e-05
time for async3: 0.00238299369812012
time for async1: 0.117094993591309
Example 2:
Starting async3
Starting async2
Starting async1
time for async2: 2.80141830444336e-05
time for async3: 0.00216799974441528
time for async1: 0.114436984062195
Example 3:
Starting async1
Starting async3
Starting async2
time for async2: 1.60336494445801e-05
time for async3: 0.00220298767089844
time for async1: 0.129496037960052
我不明白为什么这些街区不按FIFO顺序启动。有人可以解释一下我在这里缺少什么吗? 我知道它们将同时执行,但它声明并发队列将尊重FIFO以开始执行任务,但不能保证哪一个先完成。所以至少起始任务语句应该以
开头Starting async1
Starting async3
Starting async2
并且这个完成语句是随机的:
time for async2: 4.1961669921875e-05
time for async3: 0.00238299369812012
time for async1: 0.117094993591309
和随机完成报表。
答案 0 :(得分:3)
并发队列运行您提交给它的作业并发这就是它的用途。
如果您希望队列按FIFO顺序运行作业,则需要一个串行队列。
我看到你所说的文件声称这些工作将以FIFO顺序提交,但你的测试并没有真正确定它们的运行顺序。如果并发队列有2个线程可用,但只有一个处理器可以运行这些线程,它可能会在有机会打印之前换掉其中一个线程,运行另一个作业一段时间,然后再返回运行第一个线程工作。在换出之前,无法确保作业运行到最后。
我认为print语句不会为您提供有关作业启动顺序的可靠信息。
答案 1 :(得分:0)
cqueue是一个并发队列,它几乎同时将你的工作块调度到三个不同的线程(它实际上取决于线程的可用性),但你无法控制每个线程的时间线程完成工作。
如果要在后台队列中以串行方式执行任务,则使用串行队列会更好。
let serialQueue = DispatchQueue(label: "serialQueue")
Serial Queue仅在您的上一个任务完成时才会启动队列中的下一个任务。
答案 2 :(得分:0)
使用并发队列,您可以有效地指定它们可以同时运行。因此,虽然它们以FIFO方式添加,但是在这些不同的工作线程之间存在竞争条件,因此您无法保证哪个将首先达到其各自的print
语句。
所以,这提出了一个问题:为什么你关心他们打印各自的打印声明的顺序?如果订单非常重要,则不应使用并发队列。或者,另一种说法是,如果要使用并发队列,请编写不依赖于它们运行顺序的代码。
你问:
当一个Task从队列中出队时,你会建议一些方法来获取信息,以便我可以记录它以获得FIFO顺序。
如果您正在询问如何在真实应用程序中享受并发队列上任务的FIFO启动,则答案是“您没有”,因为上述竞争条件。使用并发队列时,切勿编写严格依赖于FIFO行为的代码。
如果你问的是如何根据经验从纯粹的理论目的来验证这个问题,那就做一些捆绑CPU并将它们逐个释放出来的东西:
// utility function to spin for certain amount of time
func spin(for seconds: TimeInterval, message: String) {
let start = CACurrentMediaTime()
while CACurrentMediaTime() - start < seconds { }
os_log("%@", message)
}
// my concurrent queue
let queue = DispatchQueue(label: label, attributes: .concurrent)
// just something to occupy up the CPUs, with varying
// lengths of time; don’t worry about these re FIFO behavior
for i in 0 ..< 20 {
queue.async {
spin(for: 2 + Double(i) / 2, message: "\(i)")
}
}
// Now, add three tasks on concurrent queue, demonstrating FIFO
queue.async {
os_log(" 1 start")
spin(for: 2, message: " 1 stop")
}
queue.async {
os_log(" 2 start")
spin(for: 2, message: " 2 stop")
}
queue.async {
os_log(" 3 start")
spin(for: 2, message: " 3 stop")
}
您将能够看到最后三个任务以FIFO顺序运行。
如果你想确切地确认GCD正在做什么,另一种方法是引用libdispatch source code。它无疑是相当密集的代码,所以它并不是很明显,但如果你有野心的话,你可以深入研究它。
答案 3 :(得分:0)
“我不明白为什么这些块不按FIFO顺序启动”你怎么知道它们没有?它们按FIFO顺序启动!
问题是你无法测试。事实上,测试它的概念是不连贯的。最快的是你可以测试任何东西是每个块的第一行 - 并且通过那个时间,另一个代码来自另一个是完全合法的>阻止执行,因为这些块是异步。这就是异步意味着。
所以,他们以FIFO顺序启动,但不能保证在给定多个异步块的情况下,它们的第一行将被执行。