Race condition, Synchronize, Dead Lock 등의 문제가 발생할 수 있기 때문에 동기화 객체를 신중하게 사용해야 함
메모리 가시성 : 한 Thread에서 변경한 값이 다른 Thread에서 제대로 관측할 수 있는가
메모리 장벽 : 이 시점을 기준으로 메모리 가시성을 보장
동기화 객체들이 lock상황을 점검하기 위해서는 결국 main memory를 참조해야 하므로 동기화 객체를 사용하는 시점에서는 메모리 가시성이 보장된다는 것을 짐작할 수 있음
Kernel mode는 OS에서 관리하는 상태로 context change가 발생하는 동안 overhead가 존재
User mode는 application이 작동하는 상태로 굳이 context change가 발생하지 않으므로 속도가 빠름
Mutex, semaphore 등은 Kernel mode에서 작동하지만 critical section은 user mode에서 작동
다만 Kernel mode에서 작동하는 동기화 객체들은 OS에서 해제되지 않고 Thread가 종료되거나 같은 Thread가 동일한 Mutex 획득을 하는 경우 등에서 발생하는 Dead lock을 관리해주지만 user mode에서는 직접 관리해야 함
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
EnterCriticalSection(&cs);
// Do Something
LeaveCriticalSection(&cs);
DeleteCriticalSection(&cs);
User Mode에서 작동
빠름
Process 단위로 작동
cpp 방식
std::mutex m;
m.lock();
// Do something
m.unlock();
std::mutex m;
void func(std::mutex& m)
{
// 자동으로 lock-unlock
std::lock_guard<std::mutex> lg(m);
}
Windows 방식
CreateMutex();
WaitForSingleObject();
// Do something
ReleaseMutex();
CloseHandle();
Kernel mode에서 작동
OS단위로 생성(다수의 Process를 생성할 수 없음)
외부에서 Process에 접근시 제어 가능(RPC가 가능한 듯)
CreateEvent();
SetEvent();
WaitForSingleObject()
// Do something
ResetEvent();
CloseHandle()
다수의 Thread에서 동시에 event를 받을 수 있음(Thread간의 통신용으로 사용 가능)
생성 시 자동 Reset Event와 수동 Reset Event를 설정할 수 있는데, 자동 설정 시 WaitForSingleObject()가 수행되는 즉시 자동으로 ResetEvent()를 수행
std::condition_variable cv;
cv.notify_one();
cv.notify_all();
void func(std::mutex* m)
{
std::unique_lock<std::mutex> lk(*m);
cv->wait(lk, Predicate); // lambda 함수 같은 조건 객체
// Do something
lk.unlock();
}
n개의 Thread에서 동시에 Mutex key를 획득 할 수 있음
동시에 접근 가능한 Thread의 개수를 제한하는 효과를 갖지만 Thread Safe하지는 않은 듯
CreateSemaphore();
WaitForSingleObject(); // Semaphore의 접근 가능 개수 1개 감소
// Do something
ReleaseSemaphore(); // Semaphore의 접근 가능 개수 1개 증가
Semaphore의 개수를 0개로 시작하고 ReleaseSemaphore()를 호출하면 WaitForSingleObject()로 Semaphore의 획득이 가능할까?
InterlockedIncrement();
InterlockedDecrement();
// ...
std::atomic<type> var(InitialValue);
var++;
var--;
// ...
정수형 또는 포인터 타입에 대한 산술 연산을 Atomic하게 수행
Event를 이용하여 통신 socket을 select하는 방법
Socket은 수동 Reset Event만 가능함
WSACreateEvent();
WSAEventSelect(Socket, Event, NetworkEvent(FD_Accept 등)); // Event와 Socket binding
// waitall이 true일 경우 관리하는 모든 event가 발생해야 반환
int index = WSAWaitForMultipleEvents(관리할 개수, EventArray, waitAll, timeout, altertable);
// index는 EventArray를 시작주소로하여 Event가 발생한 지점의 Index 값
if(index != WSA_WAIT_FAILED) return;
if(index != WSA_WAIT_TIMEOUT) return;
WSANetworkEvents networkEvent;
WSAEnumNetworkEvents(Socket, Event, &networkEvent);
if(networkEvent.lNetworkEvents == NetworkEvent(FD_Accept 등))
{
// Do something
}
만약 EventArray에서 다수의 Event가 동시에 발생한다면 가장 작은 index값을 반환
index는 EventArray로 주어진 주소를 시작주소로하여 event가 발생한 Array의 위치를 상대주소로 반환
즉, 만약 EventArray + 5를 시작주소로 하여 index = 1을 반환받는다면 EventArray에서는 7번 element에서 event가 발생했다는 뜻
다수의 Event를 관리하기 위해서는 반복적으로 점검해야 하는데 이 때 범위에 대하여 주의해야 함
반복으로 접근하던, 재귀적으로 접근하던 상관은 없을 듯