Based on my printed output in the console window, the work in que2
was only executed after the que1
fully finished its work, so my question is why did I get the Data race
warning even though the first block of work in que1
was completely synchronous?
Data race in closure #2 () -> () in BlankSwift at BlankSwift.porsche : BlankSwift.Car
struct Car {
var name: String
}
let que1 = DispatchQueue(label: "que1", qos: .background)
let que2 = DispatchQueue(label: "que2", qos: .userInteractive)
var porsche = Car(name: "Porsche")
for i in 0...100 {
que1.sync {
porsche.name = "porsche1"
print(porsche.name)
porsche.name = "Porsche11"
print(porsche.name)
if i == 100 { print("returned ")}
}
que2.async {
porsche.name = "porsche2"
print(porsche.name)
porsche.name = "Porsche22"
print(porsche.name)
}
}
While que1.sync
is indeed called synchronously, que2.async
is asynchronous on a different queue, so it schedules its closure and immediately returns, at which point you go to the next iteration of the loop.
There is some latency before the que2
closure begins executing. So for example, the closure for que2.async
that was scheduled for iteration 0, is likely to start executing while que1.sync
is executing for some later iteration, let's say iteration 10.
Not only that, que2
may well have multiple tasks queued up before the first one begins. It's a serial queue, because you didn't specify the .concurrent
attribute, so you don't have to worry about que2
tasks racing on another que2
closure access of porsche.name
, but they definitely can race on que1
closure accesses.
As for output ordering, ultimately the output will go to FileHandle.standardOutput
to which the OS has a buffer attached, and you don't know what kind of synchronization scheme the OS uses to order writes to that buffer. It may well use it's own call to DispatchQueue.async
to ensure that I/O is done in a sensible way, much the way UI updates on AppKit/UIKit have to be done on the main thread.