Reading this I learn that:
Instances of value types are not shared: every thread gets its own copy.* That means that every thread can read and write t
Because Array
is a value type, you're guaranteed that it has a single direct owner.
The issue comes from what happens when an array has more than one indirect owner. Consider this example:
Class Foo {
let array = [Int]()
func fillIfArrayIsEmpty() {
guard array.isEmpty else { return }
array += [Int](1...10)
}
}
let foo = Foo();
doSomethingOnThread1 {
foo.fillIfArrayIsEmpty()
}
doSomethingOnThread2 {
foo.fillIfArrayIsEmpty()
}
array
has a single direct owner: the foo
instance it's contained in. However, both thread 1 and 2 have ownership of foo
, and transitively, of the array
within it. This means they can both mutate it asynchronously, so race conditions can occur.
Here's an example of what might occur:
Thread 1 starts running
array.isEmpty
evaluates to false, the guard passes, and execution will continue passed it
Thread 1 has used up its CPU time, so it's kicked off the CPU. Thread 2 is scheduled on by the OS
Thread 2 is now running
array.isEmpty
evaluates to false, the guard passes, and execution will continue passed it
array += [Int](1...10)
is executed. array
is now equal to [1, 2, 3, 4, 5, 6, 7, 8, 9]
Thread 2 is finished, and relinquishes the CPU, Thread 1 is scheduled on by the OS
Thread 1 resumes where it left off.
array += [Int](1...10)
is executed. array
is now equal to [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]
. This wasn't supposed to happen!