actor에서 await 호출 이후 내부 상태 가정 금지
await
를 호출한 이후, actor 의 상태는 크게 변할 수 있습니다.actor MyDownloader {
var counter = 0
func download(url: URL) async throws -> Data {
counter += 1 // counter 1 올리고
let num = counter // counter 를 num 에 할당
let result = try await URLSession.shared.data(from: url) // await 로 URLSession 함수 호출
print("num : " + num.description + ", " + "counter : " + counter.description)
print(num == counter) // counter 를 그대로 num 에 할당했으니까 true.. 일까나?
return result.0
}
}
await 이후 counter의 상태를 예상할 수 없기 때문에, 모두 같은 값이 print될 것이라 예측할 수 없습니다. Task1에서 actor의 메소드를 실행하던 중, await을 만나면 suspend됩니다. 이때 다른 Task가 actor에 접근해 내부 프로퍼티를 변경할 수도 있기 때문에, 같은 값을 예상할 수 없는 것입니다.
actor 에서 많은 양의 작업을 진행하지 않는다
작업을 쪼개 병렬화한 모습
<aside> 💡 actor hopping actor의 동작은 cooperative thread pool에서 수행되는데, 한 actor에서 다른 actor로 전환되는 작업
</aside>
먼저, sports feed actor가 한 thread에서 실행되다, database actor의 save 함수를 호출합니다.
이때 database actor는 사용되고 있지 않은, uncontended 상황입니다.
따라서 thread는 sports feed actor에서 database actor로 곧장 이동(hop)이 가능합니다.
여기서 알 수 있는 사실은, actor를 전환(hop)하면서 이 thread는 block되지 않았고, hopping은 다른 thread를 필요로 하지 않았다는 점입니다.
이제 database actor의 D1이 실행되던 도중, weather feed actor가 database actor의 save함수를 호출합니다. 이것을 처리하기 위해 database actor에 대한 새로운 work item D2를 생성합니다.
하지만 이때는 실행 중인 work item D1이 있기 때문에, D2는 보류 상태가 됩니다. 따라서 weather feed actor 또한 suspend 되어 해당 thread는 다른 일인 health feed를 수행합니다. 이후 D1작업이 완료되면, runtime이 보류중인 D2를 실행할지, 다른 feed actor를 실행할지, 아니면 아예 다른 작업을 실행할지 결정할 수 있습니다.
이때 우선순위가 높은 작업을 먼저 수행하는 것이 중요한데, actor reentrancy의 개념으로 인해 시스템이 일의 우선 순위를 잘 반영할 수 있도록 설계되어 있습니다.