Concurrency using Actors | Swift 5.5 | Async/Await | Data Race
data inconsistency
발생할 수 있음.class Flight {
let company = "Luft Hansa"
var availableSeats = ["1A", "1B", "1C"]
func getAvailableSeats() -> [String] {
availableSeats
}
func bookSeat() -> String {
return availableSeats.removeFirst()
}
}
private func testFlightProblem1() {
let flight = Flight()
let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")
queue1.async {
let bookedSeat = flight.bookSeat()
print("Booked seat is \(bookedSeat)")
}
queue2.async {
let availableSeats = flight.getAvailableSeats()
print("Available Seats \(availableSeats)")
}
}
Booked seat is 1A
Available Seats ["1A", "1B", "1C"]
class Flight {
let company = "Luft Hansa"
var availableSeats = ["1A", "1B", "1C"]
let barrierQueue: DispatchQueue = DispatchQueue(label: "barrierQueue", attributes: .concurrent)
func getAvailableSeats() -> [String] {
barrierQueue.sync(flags: .barrier) {
return availableSeats
}
}
func bookSeat() -> String {
barrierQueue.sync(flags: .barrier) {
return availableSeats.removeFirst()
}
}
}
Booked seat is 1A
Available Seats ["1B", "1C"]
data inconsistency
와 같은 동시성 문제를 신경쓸 필요가 없음let
, var
과 같은 데이터의 가변성에도 영향을 받음 - 상수라면 문제 없이 참조 가능하지만, 값이 바뀔 수 있는 변수라면 별도의 블럭을 추가해야 함var
임)nonisolated
라는 키워드를 통해 컴파일러에게 해당 데이터는 var
임에도 불구하고 let
과 같이 레이스 문제가 일어나지 않는다고 강제할 수 있음async await
와 함께 액터 값을 조회/참조/변경 가능Task
블럭 내부 비동기 구문 작성actor Flight {
let company = "Luft Hansa"
var availableSeats = ["1A", "1B", "1C"]
func getAvailableSeats() -> [String] {
return availableSeats
}
func bookSeat() -> String {
return availableSeats.removeFirst()
}
}
private func testFlightProblem2() {
let flight = Flight()
let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")
queue1.async {
Task {
let bookedSeat = await flight.bookSeat()
print("Booked seat is \(bookedSeat)")
}
}
queue2.async {
Task {
let availableSeats = await flight.getAvailableSeats()
print("Available Seats \(availableSeats)")
}
}
}
Task
, async await
를 통해 비동기 구문 블럭 사용Available Seats ["1A", "1B", "1C"]
Booked seat is 1A
queue2
의 블럭이 먼저 실행되었음private func testFlightProblem3() {
let flight = Flight()
let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")
queue1.async {
Task { [weak self] in
let bookedSeat = await flight.bookSeat()
print("Booked seat is \(bookedSeat)")
self?.updateLabel(seat: bookedSeat)
}
}
queue2.async {
Task {
let availableSeats = await flight.getAvailableSeats()
print("Available Seats \(availableSeats)")
}
}
}
Task
의 비동기 구문은 현재 메인 스레드에서 실행되고 있지 않지만, 조회한 데이터를 UI로 갱신해야 하는 상황@MainActor
private func updateLabel(seat: String) {
queueOneLable.text = seat
}
@MainActor
을 따르는 함수이기 때문에 해당 태스크는 디스패치 메인 큐에서 실행, 즉 UIKit과 연결된 메인 스레드에서 실행되는 게 확실하게 보장