운영 체제는 active thread list와 blocked thread list의 링크드리스트를 따로 관리하는데
이 상황에서는 active thread list에 있는 쓰레드를 blocked thread list로 옮김
CPU가 1개일 때는 쓰레드 2개가 동시에 일을 할 때 퀀텀 타임으로 시간을 나눠서 일을 함 그래서 위에서 설명한 active thread list에서 blocked thread list로 쓰레드를 옮길 때 일이 끝날 가능성이 없음 (왜냐하면, 동시에 실행되지 않으므로)
그러나 CPU가 2개 이상일 때는 OS가 쓰레드를 옮길 때 작업이 끝날 수 있으므로,
EnterCriticalSection을 한 번 시도하는 것이 아닌 1000~2000번 시도하는 것이 성능 향상에 도움이 된다.
멀티코어의 경우 초기화할 때 InitializeCriticalSectionAndSpinCount 함수를 적고, 두 번째 인자로 횟수를 넘겨주면 된다.
그러면 멀티코어에서는 확실히 성능 향상을 볼 수 있음
싱글코어에서는 두 번째 인자는 무시됨
Mutex
공유 자원을 하나의 스레드가 독점적으로 사용할 수 있게 해주는 동기화 객체
뮤택스의 실생활 예시)
화장실 1칸이 있는데 당연하게도 한 명씩밖에 사용할 수 없음 열쇠를 가지고 화장실에 들어가서 볼일을 본 후 나와서 다시 걸어두면 다음 사람이 열쇠를 가지고 들어감
여기서 열쇠는 자원을 독점할 수 있는 열쇠
Mutex 커널 오브젝트를 만든다.
이 함수의 반환값으로 핸들을 반환하며 KMUTANT라는 이름으로 운영체제가 관리
뮤택스의 특징으로는 소유자와 소유 횟수라는 값을 가진다.
lpMutexAttributes - 보안 속성, 0
lpName - 정할 이름
dwFlags - 이 프로세스가 소유할건지, 안할건지
dwDesiredAccess - 권한, MUTEX_ALL_ACCESS
소유자가 있으면 논 시그널, 소유자가 없으면 시그널이 된다.
WaitForSingleObject로 Mutex를 통과 시 쓰레드가 Mutex를 소유하게 된다.
그러면 시그널 필드가 논 시그널이 되고, 소유자는 내가 되며, 소유 횟수가 1 증가한다.
프로그램을 한 번 더 실행하면 동일한 이름으로 또 뮤택스를 만들텐데
윈도우에서는 동일한 이름의 커널 오브젝트를 만들 수 없다.
그래서 그런 경우 기존 뮤택스를 오픈해주고 참조계수가 1이 증가한다. (MKO)
소유권을 포기하려면 ReleaseMutex를 해주어야 한다.
여러 쓰레드가 대기중인 경우 순서대로 소유권을 가질까?
먼저 내기했던 게 먼저 소유권을 잡는다는 보장이 없다. 대기중인 것 중 하나가 깨어난다 라고 봐야 함
뮤택스를 소유하고 있는 쓰레드가 WaitForSingleObject를 또 하면 그냥 통과한다.
대신 Release도 2번 해줘야 함
뮤택스를 가지고 있는 쓰레드가 소유권을 반환하지 않고 종료된 경우,
ABANONED(버려진, 포기된) 뮤택스가 된다.
이 땐 뮤택스의 소유권이 자동으로 포기되는데, 공유 자원에 문제가 있을 수 있음
Semaphore
위의 뮤택스와 비슷한데 화장실이 n개 있는 형태
자원의 개수를 관리하는 동기화 객체
자원을 한정적인 수량만큼 공유함
CreateSemaphoreEx 함수로 만든다.
반환값은 커널 오브젝트 KSEMAPHORE의 핸들을 반환한다.
세마포어만의 특징은 현재 카운트와 최대 카운트가 있다.
lpSemaphoreAttributes - 보안 속성, 0
lInitialCount - 현재 카운트값
lMaximumCount - 최대 카운트값
lpName - 만들 이름
dwFlags - 사용되지 않음, 0
dwDesireAccess - 권한, SEMAPHORE_ALL_ACCESS
WaitForSingleObject를 통과하면 소유권을 갖는다.
그리고 세마포어 객체의 카운트가 -1이 되고, 그 값이 0이면 논 시그널 상태가 된다.
ReleaseSemaphore 함수를 통해 소유권을 포기하고, 빈 자리가 생기면 다른 쓰레드가 들어온다.
Event
상태를 이용해서 n개의 쓰레드가 서로간 통신할 수 있게 해주는 동기화 객체
두 개의 쓰레드가 있는데,
하나는 그림을 다운로드하는 쓰레드고,
나머지 하나는 그 그림을 출력하는 쓰레드라고 한다면
그림을 출력하는 쓰레드는 그림이 다운로드되었는지 알아야만 한다.
이걸 신호를 기준으로 통신할 수 있는데 이 방식을 이벤트라고 한다.
하나의 쓰레드가 작업이 완료되었음을 다른 쓰레드에게 알리기 위해 사용한다.
lpEventAttrivutes - 보안 속성, 0
lpName - 정할 이름
dwFlag - 초기 시그널 상태와 reset의 종류 (0이면 논 시그널 상태에 AUTO RESET을 사용한다는 의미)
dwDesiredAccess - 권한, EVENT_ALL_ACCESS
반환값으로 이벤트 커널 오브젝트가 반환됨
이벤트의 고유한 특징으로는 3번째 인자 Reset의 종류가 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <process.h>
#include <tchar.h>
using namespace std;
HANDLE hEvent = 0;
UINT __stdcall foo(void* p)
{
WaitForSingleObject(hEvent, INFINITE); // 이벤트가 시그널될 때까지 대기
//일을 하려면 Event객체가 signal 되야 함
// 외부에서 signal상태로 바꿔줘야 함
printf("foo start work\n");
return 0;
}
int main()
{
hEvent = CreateEventEx(0, _T("MyEvent"), 0, EVENT_ALL_ACCESS);
HANDLE hThread = (HANDLE)_beginthreadex(
0, 0, foo, 0, 0, 0);
getchar();
SetEvent(hEvent); // 이벤트를 signal 상태로 만들기
getchar();
CloseHandle(hEvent);
return 0;
}
AUTO RESET은 WaitForSingleObject 통과 시 자동으로 시그널 필드가 리셋된다.
MANUAL RESET의 경우 WaitForSingleObject 통과해도 시그널 필드 변화 없음
댓글