func deposit(amount: Int) -> Int BankAccount Considering situation there are thread A and bank account instance. when only thread A access bank account there will be no problem. balance 1000 deposit 1000 -> balance 2000 balance 2000 deposit 1000 -> balance 3000
func deposit(amount: Int) -> Int BankAccount In this case, behavior become unpredictable. For example, when thread A and B access at the same time, read 1000 and deposit value at the same time, then balance may become 2000. balance 1000 deposit 1000 -> balance 2000 balance 1000 deposit 1000 -> balance 2000
func deposit(amount: Int) -> Int BankAccount The result is huge problem ( where my deposit goes… ) balance 1000 deposit 1000 -> balance 2000 balance 1000 deposit 1000 -> balance 2000 Deposit 2000 But only 1000 was added😱
_ in let balance = dataRaceBankAccount.deposit(amount: 1000) print("dataRaceBankAccount: \(balance)") } DispatchQueue.concurrentPerform will use threads and perform its prop process concurrently. So we can produce situation for data race.
DispatchQueue(label: UUID().uuidString) // serial queue private var balance = 0 func deposit(amount: Int, completion: @escaping ((Int) -> Void)) -> Void { queue.async { [self] in self.balance += amount completion(self.balance) } } } Above class have serial queue, and we can access balance via that queue, which means we can access its balance only via same thread.
doesn’t force call completion handler which means we can make bug easily • deep nest • difficult to read because of out of order Async function with async / await https://speakerdeck.com/hironobuiga/20210625-meet-async-await-at-swiftai-hao-hui?slide=17 • it force returning value • no nest, very simple • very simple order from top to bottom
to deal with concurrent computation. It defines some general rules for how the system’s components should behave and interact with each other. https://www.brianstorti.com/the-actor-model/ Hmmm…. Above definition is too abstract, so let’s check swift usage.
increase() { count += 1 } } • Reference Type • don’t support inheritance • enforce synchronized access to its mutable property / mutation method ◦ it prevent data racing
increase() { count += 1 } } counter.increase() // compile error await counter.increase() Actor enforce synchronized access, so we can access mutable data only via async / await.
deposit(amount: Int) async -> Int { balance += amount return balance } } We can define actor class with using `actor`. As you can see, the definition is very simple and almost same as not actor one.
5.5. But it depends on OS currently. We can use concurrency with above iOS15… Currently swift contributor works on back to older version. And it seems that we will be able to use Swift new concurrency under iOS version under 15. https://forums.swift.org/t/will-swift-concurrency-deploy-back-to-older-oss/49370
concurrently, data race and race condition may happen. We usually deal with this kind of problem with using fixed queue. And from Swift 5.5 we can use `Actor` as new way to solve it.