하나의 운영체제 내에서는 둘 이상의 프로세스가 생성되고, 하나의 프로세스 내에서는 둘 이상의 쓰레드가 생성된다.
운영체제가 만드는 리소스의 유형 - 프로그램의 실행과 관련된 프로세스와 쓰레드 - 입출력의 도구가 되는 소켓과 파일 - 쓰레드간 동기화의 도구로 사용되는 세마포어 뮤텍스
커널 오브젝트의 소유자 - 커널 오브젝트의 생성, 관리 및 소멸은 운영체제가 담당한다. - 즉, 커널 오브젝트의 소유자는 운영체제이다.
윈도우 기반의 쓰레드 생상
리눅스와 달리 윈도우의 쓰레드는 쓰레드 함수를 반환하면 자동으로 소멸
#include <process.h>
unsigned WINAPI ThreradFunc(void *arg);
int main(int argc, char *argv[])
{
HANDLE hThread;
unsigned threadID;
int param = 5;
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)¶m, 0, &threadID);
// ThreadFunc -> WINAPI 쓰레드 함수, ㅔparam 매개변수, threadID 리턴값
if(hThread == 0)
{
puts("_beginthreadex() error");
return -1;
}
Sleep(3000);
puts("end of main");
rerturn 0;
}
unsigned WINAPI ThreadFunc(void *arg) // 쓰레드 함수
{
//쓰레드가 실행할 프로세스
int i;
int cnt =*((int*)arg);
for (i = 0 ; i<cnt; i++)
{
Sleep(1000); puts("running thread");
}
return 0;
}
Critical Section 과 동기화 기법
CRITICAL SECTION (임계영역) - 베타적 접근(한 순간에 하나의 쓰레드만 접근)이 요구되는 모든 쓰레드가 공유해야하는 리소스에 접근하는 코드 블록 - 임계영역에 접근하기 위해서는 하나의 쓰레드가 모든 작업을 완전하게 마친 후에 접근이 가능해야함 -> 쓰레드 동기화
유저모드 동기화 - 운영체제에 의해서 이뤄지는 동기화가 아닌, 순수 라이브러리에 의존해서 완성되는 동기화 기법 - 운영체제에 의해서 제공되지 않으므로 커널모드로의 전환이 불필요 -> 상대적으로 가볍고 속도가 빠르다. - 커널모드의 기능을 사용 불가능하기 때문에 기능상 제한되는 부분이 있음
커널모드 동기화 - 커널모드 동기화는커널에 의해서 제공이 되는 동기화 기법이다. - 커널에 의해 제공되는 동기화이다 보니,다양한 기능이 제공 - Dead-lock에 걸리지 않도록타임아웃을 지정 가능
Mutex 를 이용한 동기화
커널 오브젝트의 생성
커널 오브젝트의 소멸
Mutex 오브젝트의 획득과 반납 - Mutex 오브젝트는 커널 오브젝트이기 때문에 획득을 위해서 별도로 정의된 함수가 없다. - 단, WaitForSingleObject 함수를 이용한다. --> Mutex 오브젝트는 획득 가능한 상태가 되면, signaled 상태가 된다.
WaitForSingleObject(hMutex, INFINITE);
// 임계영역의 시작
// 독점할 프로세스 내용
// 임계영역의 끝
ReleaseMutex(hMutex);
Mutex 의 획득과 반납 구조도
Mutex 기반의 동기화 예 - SyncMutex_win.c
long long num = 0;
HANDLE hMutex;
int main(int argc, char *argv[])
{
HANDLE tHandles[NUM_THREAD];
int i;
hMutex = CreateMutex(NULL, FALSE, NULL); //signaled 상태, 이름없는 Mutext오브젝트 생성
for (i = 0; i<NUM_THREAD; i++)
{
{// 쓰레드 실행
if(i%2)
tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
else
tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
}
}
WaitForrMultipleObjects(NUM)THREAD, tHandles, TRUE, INFINITE); //임계영역 시작
CloseHandle(hMutex); //임계영역 종료
printf("result: %lld \n", num)
return 0
}
unsigend WINAPI threadInc(void * arg)
{
int i;
//signaled 상태 ->wait함수로 non-signaled 변환후 임계 접근
WaitForSingleObject(hMutex, INFINITE);
for(i = 0; i<5000000; i++)
num+=1;
ReleaseMutex(hMutex); //반납 (signaled)
return 0;
}
unsigned WINAPI threradDes(Void * arg)
{
int i;
// theadInc에서signaled 상태로 변환시키면 wait 함수가 반환 임계영역 접근 가능
WaitForSingleObject(hMutex, INFINITE);
for(i=0; i<5000000; i++)
num-=1;
ReleaseMutes(hMutex);
return 0;
멀티쓰레드 기반의 다중접속 서버
//chat_serv.c
int clntCnt = 0;
SOCKET clntSocks[MAX_CLNT];
HANDLE hMutex;
while(1)
{ // 하나의 쓰레드 생성
clntAdrSz=sizeof(clntAdr);
hClntSock=accpet(hServSock, (SOCKADDRR*)&clntAdr, &clntAdrSz);
//클라이언트의 정보가 저장된 공간에 접근하는 것을 동기화
WaitForSingleObject(hMutex, INFINITE);
clntSocks[clntCnt++]=hClntSock;
ReleaseMutex(hMutex);
hThread = (HANDLE)_beginthreadex(NULL, 0, HandleClnt, (void*)&hClntSock, 0, NULL);
printf("Connected client IP: %s \n", inet_ntoa(clntAd.sin_addr);
}
unsigned WINAPI HandleClnt(void *arg)
{
SOCKET hClntSock =*((SOCKET*)arg);
int strLen = 0, i;
char msg[BUF_SIZE];
while((strLen=recv(hClntSock, msg, sizeof(msg), 0))!=0)
SendMsg(msg, strLen);
WaitForSingleObject(hMutex, INFINITE);
for(i=0; i<clntCnt; i++) //remove disconnected client
{
if(hClntSock == clntSocks[i])
{
while(i++<clntCnt-1)
clntSocks[i] = clntSocks[i+1];
break;
}
}
clntCnt--;
ReleasMutex(hMutex);
closesocket(hClntSock);
return 0;
}
//모두에게 메세지를 보내는 함수
void SendMsg(char *msg, int len) //echo to all
{
int i;
// signaled -> non-signaled
WaitForSingleObject(hMutex, INFINITE);
for(i=0; i<clntCnt; i++)
send(clntSocks[i], msg, len, 0);
ReleaseMutex(hMutex); non-signaled -> signaled
}
//하나의 뮤텍스를 대상으로 두 영역에서 동기화를 진행