Brown 연구진, async/await 런타임 7종의 관찰 가능한 의미론을 9개 설계 축으로 분류
- 7개 비동기 런타임에서 백그라운드 로그 작업을 띄우는 짧은 의사코드와 그 변형 3개를 비교한 결과, 어느 두 런타임도 같은 출력을 내지 않았음
- 논문은 straight-line asynchrony 구현의 관찰 가능한 의미론을 9개 설계 축으로 분류했으며, 시작 단계의 Eagerness·Suspension, 종료 단계의 Extent·참조 강도·정리·예외 전파, 취소의 Awareness·Direction·Persistence를 제시함
- Eagerness에서 Python과 Rust는 async 함수 호출이 실행하지 않는 코루틴을 만드는 Lazy 방식이고, C#과 JavaScript는 현재 스레드에서 평가한 뒤 await 시 작업을 예약하는 Eager 방식임
- Swift와 Python의 Trio는 Dynamic Extent를 택해 함수 안에서 생성한 작업이 그 함수를 벗어나 존재할 수 없으며, JavaScript·C#·Tokio·Smol·Asyncio는 기본적으로 런타임 종료까지 작업이 남을 수 있는 Indefinite Extent임
- 취소를 인지하는 런타임 중 Asyncio는 작업이 취소를 무시하고 계속 진행하면 취소 상태가 사라지는 Transient cancellation이고, Trio와 Swift는 취소를 무시해도 취소 상태가 지속됨
Hacker News opinions
이런 프로그램을 언어별로 번역하되 각 런타임의 의미를 제대로 보존하는지 보는 코딩 에이전트 벤치마크가 있으면 재밌겠음.
9개 축을 잡았으니 9차원 거리로 런타임 조합의 유사도와 차이를 계산할 수도 있겠네. 한 비동기 시스템을 아는 사람이 다른 시스템을 배울 때 비교 도구로도 좋겠음.
퀴즈에서 자바스크립트 개발자라고 나오긴 했는데, 난 async/await 자체가 싫어서 루비를 택한 쪽임. await가 정지를 뜻하지 않는다면 대체 뭘 뜻하는 건지 모르겠고, 스레드의 의미론은 언어와 런타임을 넘어 더 잘 옮겨감.
await 지점에서 반드시 멈추는지, 런타임이 멈추지 않을 수 있는지가 여기서 말하는 차이임. 비동기와 병렬성은 같은 얘기도 아니고.
이미 완료된 Future나 Promise를 await하면 실행 중인 스레드는 멈출 이유가 없음. await는 스레드를 정지하는 동의어라기보다 coreturn 기능을 효과로 표현하려는 연산에 가까움.
스레드와 async/await를 같은 프로그램에서 둘 다 써 봤는데, async/await가 더 쉽게 맞출 수 있다는 말에는 동의 못 하겠음. 내게는 스레드를 너무 많이 만들지 않으려는 방법이었고, Erlang 프로세스나 Java Loom 가상 스레드 같은 쪽이 더 나아 보임.
C#이 주력인데 퀴즈는 내가 JS 개발자라고 하더라. Jon Skeet 글을 몇십 개 읽고 반성해야 할 듯.
그래도 C#에서 main이 끝날 때 대기 중인 작업이 종료된다는 점은 보통 개발자가 잘 안 마주침. 대개 오래 실행되는 서버나 앱 안에서 작업하니까.
구현 선택이 중요하다는 건 알았지만 설계 축이 9개나 될 줄은 몰랐음. async는 작고 자족적인 언어 기능처럼 보이지만 조합마다 결과가 달라지고, 그 함의를 아직 다 이해하지 못한 것 같음.
Zig도 넣어 줬으면 했음.
Zig는 이 글이 다루는 스택리스 코루틴 기반 async/await를 현재 지원하지 않음. 예전 기능은 제거됐고, 0.16의 사용자 공간 I/O API로 그린 스레드 등은 만들 수 있지만 컴파일러 지원 없이는 스택리스 코루틴을 만들 수 없음. 전용 문법 없이 저수준 버퍼 API로 코루틴을 되살리자는 제안은 열려 있음.
난 스레드보다 async/await부터 배워서 그런지 async가 더 어렵다는 말에 공감 못 하겠음. 스레드, 고루틴, Kotlin 구조적 동시성보다 훨씬 이해하기 쉬웠음.