func fetchSomeThing() async throws -> String {
// ~~~
let someThing = try! await fetch()
// ~~~
}
함수 내부에서 URLSession을 통해서든 어떻게든 비동기적으로 함수가 실행되는 부분이 있으면,
함수가 종료되는 시점과 closure가 실행되는 시간이 다르다.
그럼 당연히 그 결과 값으로 무언가를 할려고 하면
one() {
two($0) {
three($0) {
four($0) {
// ~~~
}
}
}
one이 부르고 나온 Escaping closure의 argument로 또 다른걸 부르고 하는 형태가 반복되기 때문에
아 sync를 붙이고 await를 붙이면 그냥 그 비동기함수가 완전히 끝날때까지 기다렸다 return 값을 통해 나에게 준다 라고 생각했다.
사실 막 틀린것은 아닌것 같다.
하지만 그 스레드가 멈추고 다른 스레드에서 기다린다는 것은 아니다.
왜 await는 async한 함수, Task 안에서만 사용이 가능한가?
아니 이 await는 비동기적으로 수행되는 함수를 동기적으로 수행하게 하는 건데 왜 위와 같은 조건에서만 사용가능하지?
생각해보면 쉽다!
만약 비동기 함수를 기다리는 곳이 main thread면? , 아니면 그 비동기 함수가 끝나질 않아서 계속 기다리게 된다면?
위와 같은 상황에서는 당연히 await한 부분을 버려야 한다. 그렇기 때문에
사실 진짜로 기다리는 건 동기함수이지 비동기 함수가 아니다.
진짜 기다리는건 thumnail이다 정확히는 기다리는 것보다 나에 대한 통제권을 동기함수에게 넘기고, 그 동기함수가 끝나는 시점에 다시 실행시켜주고 통제권을 반납한다.
await는 비동기 함수가 끝나기 기다리는 것이면, 난 그 동안 block 되어 있는건가?
이건 맞았다.
하지만 block이 되어서 resource가 unlock 될 때까지 context switching을 반복하면 thread를 만들지 않는다.
아까 말햇든 통제권을 넘기는 과정이기 때문에, 시스템에 통제권을 넘기고 해당 쓰레드는 그냥 자유가 된다
자유가 된다? 이 말인 즉 await가 붙은 지점을 만나는 순간 이 쓰레드는 시스템에 의해 다뤄진다. 그래서 await 뒤에 있는 비동기 함수가 실행된다.
한번더 정리하면 비동기함수가 다른 thread에서 실행되는게 아니라 해당 thread에서 실행 된다.
기존 GCD는 잘못 됬을 때, thread explosion이 발생할 수 있다. 예를들어 fetch할게 100개 있는데
한 fetch 마다 serialQueue 에 업데이트 한다면, block이 되고 그러는 동안 또 fetch 한다. 이러는 동안 거의 계속 새로운 thread를 만드는데 이렇게 되면 thread의 갯수가 너무 많아진다.
이렇게 되면 언젠가 다시 block 되어 있는 thread를 사용해야하기 때문에 그 최신 정보를 담는 메모리에 over head가 발생하고, thread가 너무 많아져서 스케쥴링에도 문제가 생길 수 있다. (당연히도 엄청 많은 context switching 이 발생)
하지만 Swift Concurrency는 조금 다른 정책을 적용한다.
1번, CPU Core 수에 맞는 Thread만 사용하게 되면 당연히 thread 수가 부족해지는 현상이 있을 수 있다.
하지만 이 같은 문제는 위에 설명 했듯이 시스템이 block 되어 있는 Thread를 적절히 사용하면서 해결한다.
2번, 이렇게도 많은 Thread에서 처리하던 work item들이 하나의 thread에 몰리기 때문에 switching 시 문제가 생길 수 있다. 그래서 우리는 메모리에 무리를 덜 가게 하도록 한 Continuation이라는 다시 시작해야 할 부분을 표시한 continuation을 사용한다. 이는 아래와 같이 작동한다
한 함수가 potential suspend point 즉 await 라고 쓰여진 부분을 보는 순간 block된다기 보단 해당 thread 가 자유가 되어 시스템에 맞겨진다. 그리고 async 함수가 시스템에 의해 다시 실행된다고 해서 꼭 원래 돌아가 thread에서 돌아가는 건 아닐 수 있다.
해당 하는 async 함수는 potential suspend point 이고 그렇기 때문에 나머지 부분을 Continuation 객체로 만들어 list로 heap에 저장하고 해당 thread의 제어권을 system에 넘긴다.
그러다가 해당 작업이 필요해지는 시점에 한 Thread를 잡아( Core 수 이하의 즉 있던 Thread 일수도 ) Continuation을 불러 마저 실행한다.
나라는 함수가 potential suspend point를 가지고 있기 때문에, 언제든 thread 제어권을 넘겨야 할 수 도있다.
놀랍게도 컴파일 에러가 발생한다
비 독립적(non-isolated) 구문이 변할 수 있는 프로퍼티에 접근하는 것을 금지한다는 메시지
가 발생한다.
쉽게 말하면 원래 있던 Thread에서 독립적인 것들만 접근할 수 있다는 것이다.
이는 추후에 다루겠습니다~