자문자답 공부

ThreadPool

Roka_is_back 2023. 11. 24.

구현 목적.

1.비동기적으로 여러 작업들을 처리해야 할 때 사용한다.

2. 뮤텍스와 크리티컬 섹션의 속도 차이를 측정한다.

 

왜.

첫번째, Busy Thread를 방지한다. 

BusyThread란?
다수의 스레드가 계속해서 CPU 시간을 선점함으로써 다른 앱들이 충분한 시간을 획득하지 못하며
컴퓨터 성능이 나빠지는 것을 의미한다.

 

두번째, 여러 작업을 각 스레드에 분배하여 비동기 적으로 처리할 수 있다.

 

세번째, 유저모드의 CriticalSection과 커널모드의 Mutex의 속도 차이를 측정해본다.

 

 

어떻게.

작업을 Queue에 쌓아놓고 작업이 Push될 때만 Thread의 대기 상태를 해제하여 CPU를 할당하도록 하고,

해당 Thread가 작업을 수행하도록 한다.

 

 

 

(추가) 개선점 1- 임계영역 수정

임계영역은 작은 단위로 구성해야 한다.

따라서 위의 코드를 보면 work를 수행하고 나서 job을 pop 하는데,

이는 work가 오래 걸리는 작업이라면 lock 반환이 오래 걸릴 것이다.

우리는 mjob이 pop 하는 순간을 보호하고 싶은 것이므로 순서를 교정했다.

 

 

실행 결과 두 코드는 차이점을 보였는데 ,
이는 줄띄움 명령어가 출력되기 전에 다른 스레드의 문자 출력이 이루어져서 발생한 것으로 추측한다.

기존 코드
개선 코드

 

(추가) 개선점 2-  작업 처리 속도보다 작업 요청이 빠른 경우, 조건 변수 신호가 날아가버림. = lost wake


기존 방식은 Push할 때 SIGNAL을 줘서 work thread가 대기 상태에서 빠져나와 job을 수행한다.
하지만 이렇게 할때 문제Push에서 SIGNAL을 주는 시점에 마침 대기하고 있는 스레드가 있어야 하는데,
모든 스레드가 job을 수행중이라면 Push가 발생시킨 SIGNAL은 잃어버리게 된다.

 

그렇게 되면 총 100개의 job을 수행해야 하는데 막상 80개만 처리되고,

나머지 job은 queue에 그대로 남아 있을 수 있다고 생각했다.

 

최악의 경우 데드락이 발생할 수 있고 

실제로 테스트 해봤을 때도 Push 속도가 처리 속도보다 더 빨라서 Job을 n개 수행한 뒤에는 더이상 처리하지 못했다.

 

변경 전 코드

 

38개의 작업밖에 수행하지 못했다.

해결: 만약 스레드가 작업을 수행하였다면 작업 항목을 처리한 뒤에 다시 조건변수에 SIGNAL을 주도록 했다.

당장 처음으로 작업 완료한 스레드는 이 신호를 못받겠지만 다음 작업 완료부터는 서로 서로 SIGNAL을 받을 수 있다.

 

변경 후 코드
작업을 모두 처리했다.

 

CThreadPool.cpp
0.00MB
CThreadPool.h
0.00MB
main.cpp
0.00MB

 

(추가) 개선점 3- Job 처리 동작 방식 변경. = lost wake과 spurious wake 방지

기존 동작은 1개의 Job은 1개의 Signal을 요구했다.

바뀐 동작은 Job Queue가 비었을 때만 대기 시키도록 한다. 이때, Job이 들어오면 대기가 풀린다.
이렇게 하면 2번처럼 Signal을 잃어 데드락이 걸리지 않고 동작할 수 있다.

 

CThreadPool.cpp
0.00MB
CThreadPool.h
0.00MB
main.cpp
0.00MB

 

 

시간 측정 

CriticalSection : 3~6 초 소요.

std::Mutex : 3~6초 소요.

 

왼: std::mutex  오: cs
왼:std::mutex 오:cs

둘 다 Best가 3초대 Worst가 6초대이다.(보통 4~5초대)
생각보다 차이가 많이 나지 않으며 측정때마다 누가 더 빠른지가 달라졌다.
(미세한 차이로 Critical Section이 빠르거나 mutex가 빠르게 측정된다.
보통 1초의 차이가 관측될 때는 cs가 더 빠르게 측정된다.)

 

측정때마다 다른것은 CPU 할당을 어떻게 받는지 알수 없기 때문에 그때 CPU 상황이 달랐을 수도 있다고 생각한다.

 

왜 mutex와 cs의 차이가 별로 안나는 걸까?

 

일단 Window Mutex와 std::mutex가 다르다는 점을 인지해야 한다.

window mutex

 

std::mutex

 

아래 링크는 window mutex와 std::mutex의 차이점을 알려준다.

내용에 따르면 std::mutex는 cs와 크게 다르지 않다고 한다.

 

https://stackoverflow.com/questions/36788693/what-difference-between-c-stdmutex-and-windows-createmutex

 

What difference between C++ std::mutex and windows CreateMutex

I'm programming in Windows on c++ (Visual Studio) I can create mutex using either std::mutex or CreateMutex. What is the difference between them? Which one I should prefer and which one is faster? Do

stackoverflow.com

 

std::mutex는 어떻게 구현되었길래 CriticalSection과 차이가 안날까?

아래 링크 참고

std::mutex는 내부적으로 Criticalsection을 사용한다.
이미지에 나와있듯이 window7 이하면 cs, 이상이면 슬림리더라이터락을 사용.
(SRWLock에 대해서는 제프리 Window C++ 8장에 나와있다.)

https://jungwoong.tistory.com/57

 

[c++] std::mutex

mutex는 c++ 11에서 부터 추가되었습니다. 헤더를 통해서 접근할 수 있습니다. Window의 Mutex(커널 모드)와는 이름이 동일 하지만 내부구현은 WINDOW의 CriticalSection or srwlock(유저 모드)을 통해서 구현되

jungwoong.tistory.com

 

 

댓글