본문 바로가기
C#.Net

Thread.Sleep과 Task.Delay 설명 (출처: 디씨인사이드 갤러리)

by 호야호잇 2024. 7. 16.

1

 

자 일단 소스코드는 이거야. 굳이 Sleep test하는데 웬 Task.Run? 할테지만 일단 한번 끝까지 봐바.

 

내가 말하는 내용을 모두 이해 한다면 Task.Run을 사용한 이유도 이해가 될꺼야

 

 

일단 각자 method들 설명을 좀 봐볼까?

 

먼저 Thread.Sleep이야

 

2

 

특별한거 없지? 우리가 알고있는 그 Sleep이 맞아. 밀리세크 수만큼 스레드가 블락 된대

 

다음 Task.Delay를 한번 봐볼까?

 

3

 

오 먼가 설명이 약간 달라.. 반환된 Task를 연산하기 전에 밀리세크만큼 기다린대.

일단 뭐 조금 다르다는 느낌정도지?

그리고 빨간색으로 표시된 awaitable Task가 우리가 주목해야 할 부분이야.

일단은 뒤에가서 다시 보자구.

 

 

바로 위에 코드가 있지만, 상기겸 코드를 다시 한번 봐볼까?

4

자 문제를 내볼게, 출력 순서가 어떻게 될 것 같아?

Console.WriteLine에 찍고있는 숫자를 기준으로 생각해 주면 될 것 같아.

물론 프갤에 고수형들 많으니까 내가 지금 뭐하는건지 눈치 챈 형들도 있을꺼.

이미 내용 알면 걍 살포시 뒤로가기 눌러주면 돼

 

자 이제 모르는 형들만 남았겠지? 더 진행 해볼게, 바로 출력 결과를 보여주면

5

 

출력 순서는 위와 같아.

 

 

혹시 "당연한거 아냐? 이거 말할라 했냐 ㅄ" 하는 형들은 아직까지 안나가고 머함? 빨리 뒤로가기 누르셈

 

 

만약에 async/await를 모르는 형들은 오잉? 했을꺼, 1, 2, 3, 4가 출력 될 것 같이 생겼는데 결과는 1, 2, 4, 3이지?

 

심지어 시간도 봐바. 4가 출력된 시간은 2가 출력된것과 같은 52초야

 

그리고 또 하나 주목 할 부분은, ManagedThreadID야.

 

1, 2, 4를 찍을때는 ManagedThreadID가 3인데, 헉.. 4찍을때 ThreadID 출력 안했네.. 쟤도 3이라고 찍혀 틀림없어 날 믿어.

 

 

특이하게 마지막 3: 4라고 찍히지?(녹색박스)

 

일단 이거 설명은 아래에서 하고, 대체 왜 3보다 4가 먼저 찍혔는지, 그리고 왜 또 시간은 52초에 찍혔는지 먼저 알아보자구

 

아까 Task.Delay 설명글에서 내가 빨간 박스 쳤던거 기억하지?

 

awaitable이라고 되어있던 부분 말야.. awaitable 함수는 보통 async/await 키워드와 함께 사용하거나,

 

return되는 Task를 실행 해야지 원하는 결과를 얻을 수 있어

 

자 그럼 async/await이란 무엇이냐!

 

async/await 키워드는 .net 4.0때 얼굴을 내비친 아이야..

 

내 개인적인 추측이지만, 늅늅충들이 Winform/WPF에서 하도 UI Thread로 block wait를 걸어놓고

 

UI 행걸린다고 징징대서 MS가 넣은걸로 추정됨. 그리고 go lang에서도 지원하고있고

 

c++또한 현재 구현 들어와 있어(c++은 정확히 언제 들어왔는지 잘 모르겠다..)

 

 

결론을 말해주면, async/await는 동작이 조금 특이한데, await가 걸리는 시점에서 함수 호출자는 return이 돼.

 

await의 의미를 말로 풀어보면, 

 

"이건 시간 좀 걸리는 함수니깐 넌 니 할일 하러 돌아가. 이거 함수 결과 오면 형이 아래코드들 실행하라고 말해줄겡"

 

하는 거라고 생각하면 돼

 

 

그림으로 순서를 설명해보면,

 

6

 

앞서서 출력 결과물 봤지? 실제 코드도 위와같이 실행이 돼.

 

Task.Sleep을 수행하러 들어온 (빨간색)Thread는 3에서 block wait로 1초를 기다리고, 4를 수행하지.

 

이때 동작은 말그대로 block wait야. 이때 스레드는 바보처럼 마냥 1초를 기다리게 되지.

 

(c++은 APC라도 있어서 요 스레드가 뭔가 할걸 기대라도 할 수 있지만, C#은 그런거 없어. 쟤는 그냥 ㅄ같이 걍 대기임)

 

요 Sleep을 한 Thread가 UI Thread였다고 생각해봐. 1초간 프로그램이 응답이 없었겠지?

 

안타깝게도, UI Thread한테 이런식으로 block wait를 시키는 개발자들 많더라고...

 

토달지마 내가 봤다고

 

 

다시 말하지만, 이런 증상을 해결 해 줄 수 있는게 async/await야.

 

(빨간)Thread는 5에서 await을 만난 순간, awaitable한 (녹색)Task.Delay를 프레임웤에게 실행해 달라고 부탁해.

 

물론 이때 (노랑)8번 코드 영역도 함께 던지게 되지. (녹색)7 부분 실행을 하고 나면 이후에 실행 해 달라고..

 

 

이걸 어떻게 아냐고? 다시한번 앞의 결과물을 봐볼까?

 

7

 

코드에도 나와 있지만, 그리고 다시 말하지만, 콜론으로 구분되는 두번째 공간에 찍힌 숫자의 의미는 Managed Thread ID야.

 

분명히 두번째 라인을 출력한 아이는 ManagedThreadID가 3인데,

 

동일 함수 내에 있는 세번째 Console.WriteLine을 실행한 아이의 ManagedThreadID는 4지?

 

이 결과물이 그 증거야.

 

 

 

그래도 못믿겠다고? 소설을 쓰라고?

 

그래 분명 이러는 사람들 있음.

 

그래서 더 준비함

 

8

 

위에 제공한 샘플 프로그램 빌드하고 IL을 까보면 대략 이렇게 나와

 

위에 보면 Console.WriteLine("2 : ... 실행하는 코드 아래쪽 영역인데, 

 

잘 보면, 먼가 꽁냥꽁냥 열씸히 보따리 싸서 실행지점 던져놓고 지는 바로 나가는 코드가 보이지?

 

그리고 보따리 쌀때 호출 주소 쫒아가 보면 우리가 소설을 쓰고 있었던 Console.WriteLine("3 : ... 부분이 보여

 

 

이제 대충 알겠지?

 

좀 산만한 내용을 보충하기 위해 요약을 다시 한번 하면 아래와 같아.

 

Thread.Sleep : 현재 요 Method를 수행하는 놈을 Block wait 시키고 wait가 끝났을때 동일 Thread를 하부 코드 실행하도록 풀어준다.

Task.Delay : 현재 요 Method를 수행하는 놈을(await 했을때) 바로 호출 지점으로 돌려 보내고, 다른 스레드를 통해 하부 코드를 실행 하도록 시킨다.

 

ㅇㅋ?