LISTORY

[윈도우즈 시스템 프로그래밍] 쓰레드 풀의 구현 본문

IT/윈도우 프로그래밍

[윈도우즈 시스템 프로그래밍] 쓰레드 풀의 구현

LiStoryTeller 2018. 9. 29. 21:58


뇌를 자극하는 윈도우즈 시스템 프로그래밍 책의 유투브 강의 내용 정리이다.


이번 시간엔 저번 시간에 이어 쓰레드 풀 부분을 계속해보겠다.


YouTube 주소 : 쓰레드 풀의 구현 



쓰레드 풀의 구현


쓰레드 풀의 구현 원리



위를 보고 우리가 궁금할 것은 두가지이다.


1. WORK가 뭐고 할당은 어떻게?

2. 쓰레드를 어떻게 저장했다가 가져올 것인가 (쓰레드의 관리)


이 두가지만 안다면 실제로 쓰레드 풀을 구현 가능하다.


이 두가지를 하나의 모델로 보여주길 위해 위의 코드를 제시하였지만, 이는 하나의 모델에 지나지 않다. 


더 좋은 모델도 존재하지만 이번 강의 설명을 위해 간략화한 것이다.


일단 코드를 보자




여기서 WORK는 함수 포인터이다. 즉, 일은 함수로 구현해야 한다.


우리가 할 것은 쓰레드 풀을 직접 만드는 것이기 때문에 쓰레드 정보는 따로 저장해야 한다(윈도우즈가 해주지 않기에).


우리도 쓰레드를 관리해야 하므로 __WokrThread라는 구조체를 정의했다.


여기서는 단순하게 쓰레드의 핸들과 id 정보만 저장하도록 했다.


밑에가 실질적인 ThreadPool이다.


우리가 보통 이야기하는 쓰레드 풀이라는 것은 사실 소프트웨어적인 것이다.


소프트웨어적으로 구현되는 프레임모델을 이러하다


할당되는 일을 저장하는 배열, 풀이 존재하다. 여기엔 쓰레드 정보를 담도록 한다.


즉, 쓰레드를 풀에 저장하는 것이다.


또한, 이벤트 정보를 저장할 수 있는 배열(workerEventList)도 존재한다.


이게 필요한 이유는 좀있다 설명하도록 하겠다.


어디까지 일을 가져다 썼는지, 그 인덱스 정보를 저장하기 위한 멤보도 존재한다.


이는 배열 관리를 위해 필요한 인덱스를 저장하는 것이다.


현재 pool에 존재하는 쓰레드의 개수를 저장하는 멤버도 존재한다.


정리해보자면 Pool에는 할당된 일의 정보와 쓰레드 정보가 존재한다. 이는 각각 배열로 존재한다.



worklist가 반드시 풀에 들어가야한다? 그건 아니다 여기서 그렇게 구현한 것


여하튼 이 모델에선 WorkList에 함수 단위의 일을 순차적으로 저장하고, 이를 쓰레드에 하나씩 할당해준다.


예시로 보면 단순 배열로 되어있지만, 다르게 해도 상관 없다.



실질적인 내용에 대해 들어가겠다.



쓰레드를 생성한다.


근데 쓰레드만 생성하는 게 아니라 이벤트 오브젝트도 같이 생성한다.


왜?


쓰레드가 풀에 있다는 것은 쓰레드는 일을 하지 않는다는 것을 뜻한다.


이는 곧, 해당 쓰레드가 스케줄러에 의해 스케줄링 되서는 안된다는 것을 뜻한다.


쓰레드가 일을 하기 위해 쓰레드 풀을 나갔을 경우에만 스케줄러에 의해 컨트롤 되어야 한다.


그러기 위해선 이벤트 오브젝트가 필요하다.


쓰레드를 구현할 때 쓰레드는 생성과 동시에 기본적으로 함수를 호출하게 되어있다. 


함수가 제일 먼저 하는 일은 이벤트를 바로보는 일, WaitForSingleObject() 함수를 호출하는 것이다.


근데 생성과 동시에 이벤트는 non-signaled 상태이기 때문에 쓰레드는 blocked 상태, 즉 그냥 자는 상태가 된다.


물론 자는 상태이기 때문에 이 쓰레드는 스케줄러에 의해 스케줄링되지 않는다.


이를 위해 쓰레드 풀에 쓰레드가 생성 시, 각각 이벤트 오브젝트를 생성하여 쓰레드가 blocked 상태가 되게끔 한다.


다시 정리해보겠다.


쓰레드 생성하여 배열에 저장하였다. 즉, 쓰레드 풀에 넣었다.


이 쓰레드들은 각각의 이벤트 오브젝트에 WaitForSingleObject()를 호출하도록 하여, 쓰레드를 잠재운다.


이걸 쓰레드 풀에 저장되었다고 한다.


소프트웨어적으로 보면 쓰레드를 blocked 상태에 두는 것과 같다.


이 상태에서 일이라는 것이 할당된다. 여기서 일은 WORK이고, 함수포인터이다.


그럼 쓰레드 풀은 쓰레드들을 깨운다.


하나의 WORK이 등록이 되자 쓰레드를 관리하는 쓰레드 풀에서 쓰레드 하나를 깨운다.


즉, 이벤트 오브젝트를 non-signaled상태에서 signaled상태로 바꾸어서 스레드를 깨운다.


쓰레드를 깨운 다음에는 WorkList에 있는 일을 쓰레드에게 할당해주어야 한다. 


여기서 그 역할을 하는 것이 GetWorkFromPool() 함수이다.


이 함수를 호출하면 함수 포인터 하나를 얻어 함수를 호출할 수 있다.



정리


쓰레드의 쓰레드 함수는 자기에게 할당되어 있는 이벤트 오브젝트가 signaled상태인지 확인한다.


signaled 상태가 되어 쓰레드가 풀에서 빠져나오면 work 하나 얻어서 호출한다.


그 이후 다시 WaitForSingledObject() 함수를 반복한다.


즉, 쓰레드 풀에 있는 쓰레드가 일이 생기면 나와 일을 하고, 일이 다 끝나면 다시 쓰레드풀로 돌아가는 형태이다.








Comments