목차
1. 들어가며
2. async, await
3. 동기 함수와 어떻게 다른가?
4. 마치며
들어가며
ASP.Net Core, Node.js 등으로 서비스를 만들게 되면 async, await 키워드를 사용하여 비동기 제어를 하는 것은 필수불가결하다. 이번 글에서는 async, await에 대한 개념과 헷갈리는 개념들을 정리해 보려고 한다.
Async, Await
https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
Asynchronous programming - C#
An overview of the C# language support for asynchronous programming using async, await, Task, and Task
learn.microsoft.com
async, await 키워드는 비동기 제어를 위해 사용하는 키워드이다. async 키워드는 이 함수가 비동기적으로 동작한다는 것을 의미하고 await 키워드는 이 함수의 작업이 완료 되어 응답을 받을때까지 기다리겠다는 의미이다.
만약 비동기 함수에 대해 await 키워드를 사용하지 않으면 비동기 함수 특성상 작업의 완료 유무에 상관없이 바로 값을 반환하고 다음 라인의 코드를 실행할 것이다. 위의 공식 문서에서는 이 비동기 작업을 아침식사를 만드는 것에 비유하였다.
1. 커피 한잔을 붓는다.
2. 팬을 달군 후 계란 2개를 볶는다.
3. 베이컨 세조각을 볶는다.
4. 빵 두 조각을 토스트한다.
5. 토스트에 버터와 잼을 추가한다.
1번 부터 순서대로 진행한다고 하더라도 아침 식사는 완성이 되겠지만, 실제로 아침식사를 준비한다면 우리는 어떤 식으로 하게 될까? 먼저 커피를 만들기위해 전기 포트에 물을 올린 후, 팬을 달구기 위해 불을 켠 후, 빵을 토스트기에 올릴 것이다. 시간이 흘러 팬이 달궈졌다면 계란과 베이컨을 볶을 것이고, 물이 끓는다면 커피잔에 부어줄 것이며, 토스트가 완성 되었다면 버터와 잼을 바를 것이다.
여기서 중요한것은 모든 작업들이 순서에 상관없이 먼저 완료되는 대로 처리했다는 것이다. 즉, 커피를 만들기위해서는 커피포트에 물을 올리고 끓을 때까지 기다려야하고, 계란과 베이컨을 볶기위해서는 팬이 달궈질때까지 기다려야한다. 마찬가지로 토스트에 버터와 잼을 바르기위해서는 토스트가 만들어질때까지 기다려야 한다.
'시켜 놓은 뒤에 기다리는 동작'우리는 이것을 비동기 제어에 비유할 수 있다. 동기적으로 동작한다면 이 동작이 완료될때까지 기다려야 하지만 비동기적으로 처리하면 우리는 그동안 다른 일을 할 수 있다. 그리고 이것은 프로그램을 효율적으로 돌아가게 해준다.
async, await는 이렇게 완료까지 대기가 필요한 비동기 작업들이 완료될때까지 기다린 후 응답이 왔을때 다음 라인부터 진행하게 만들어주는 키워드이다.
그리고 나는 여기서 한가지 의문이 들었다. '비동기 동작의 개념과 async, await의 개념도 이해를 했지만, 결국 async, await는 비동기 작업을 동기적으로 수행하게 만드는 키워드 아닌가? 이럴거면 동기함수를 쓰면 되는데 뭐하러 async, await를 쓰지?'
동기 함수와 어떻게 다른가?
How is async with await different from a synchronous call?
I was reading about asynchronous function calls on Asynchronous Programming with Async and Await. At the first example, they do this, which I get: Task<string> getStringTask = client.GetStrin...
stackoverflow.com
다행히도 나와 같은 의문을 가졌던 사람이 있었다. 확실히 async, await를 쓰면 비동기 제어가 쉬워지는 건 맞지만 이렇게 되면 동기함수랑 다를게 뭘까? 그 차이에 대해서 아래의 답변을 자세히 보자.
Calling await client.GetStringAsync() yields the execution to the calling method, which means it won't wait for the method to finish executing, and thus won't block the thread. Once it's done executing in the background, the method will continue from where it stopped.
번역)
calling wait client.GetStringAsync()는 호출 메서드에 대한 실행을 제공하므로 메서드가 실행을 완료할 때까지 기다리지 않으므로 스레드를 차단하지 않습니다. 백그라운드에서 실행이 완료되면 메서드가 중지된 위치에서 계속됩니다.
If you just call client.GetString(), the thread's execution won't continue until this method finished executing, which will block the thread and may cause the UI to become unresponsive.
번역)
client.GetString()을 호출하면 이 메서드가 실행을 완료할 때까지 스레드의 실행이 계속되지 않으므로 스레드가 차단되고 UI가 응답하지 않을 수 있습니다.
중요시 봐야하는것은 완료될때까지 '기다린다'라는 말의 의미이다. 동기 함수는 말그대로 쓰레드가 멈춰서서(Block)답변이 올때까지 그 자리에서 기다리게 되지만, await의 경우 쓰레드가 멈추는 것은 맞지만 답변이 올때까지 그 자리에만 있지는 않는다. 즉, 해당 자리에서 쓰레드가 멈추긴하지만 만약 다른 작업에서 일손이 부족하다면 이 쓰레드가 불려가서 일을 할 수도 있다는 것이다. 그리고 await가 선언된 비동기 작업이 끝난 경우 그 아래 라인의 작업은 또 다른 쓰레드가 와서 할 수도 있는 것이다.
보통 await를 통해 제어하는 비동기 함수들은 백그라운드에서 돌아가는 I/O작업인 경우가 보통인데, 만약 백그라운드 쓰레드에서 I/O응답을 받지 못하거나 응답이 느려지는 경우에도 프로그램 입장에서는 쓰레드를 아무것도 시키지않고 놀리는 것이 아니기 때문에 async, await가 동기적인 함수를 사용하는 것에 비해서 훨씬 많은 이득을 취할 수 있는 것이다.
마치며
이번 글에서는 async, await와 동기적인 동작과의 차이를 비교 분석해보았다. async, await가 어떤 식으로 동작하는 지는 알고 있었지만 동기함수와의 차이를 명확히 파악하고 나니 좀 더 확실히 알게 되는 계기가 되었다.
'알게 된 것' 카테고리의 다른 글
[알게 된 것] 서버 <-> 클라 연결 유지 시 서버 부담에 대해 (0) | 2025.04.05 |
---|---|
[알게 된 것] 도커 사용 시 WSL, Hyper-V 필요한 이유 (0) | 2024.10.07 |
[알게 된 것] - c++에서 연산자 '<' 재정의 시 주의점 (0) | 2024.03.15 |
[알게 된 것] - 자바와 함수 포인터 (0) | 2023.01.12 |
[알게 된 것] - reference counting(참조 횟수 계산) (0) | 2022.08.05 |