두 쓰레드가 같은 값에 동시에 접근하면,
연산 끝낸 후에 결과 저장하기 전에
다음 쓰레드가 접근해버릴 수 있기 때문에 문제 발생.
동기화 필요.
임계영역
동기화 필요한 상황
pthread_mutex_init() : 자물쇠 생성pthread_mutex_destroy() : 자물쇠 소멸pthread_mutex_lock() : 자물쇠 잠그기pthread_mutex_unlock() : 자물쇠 풀기void* thread_inc(void* arg)
{
int i;
pthread_mutex_lock(&mutex);
for(i=0; i<500000; i++)
num+=1;
pthread_mutex_unlock(&mutex);
return NULL;
}
void* thread_des(void* arg)
{
int i;
for(i=0; i<500000; i++)
{
pthread_mutex_lock(&mutex);
num-=1;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
sem_init() : 세마포어 생성sem_destroy() : 세마포어 소멸sem_post() : 세마포어 값 1 증가sem_wait()static sem_t sem_one;
static sem_t sem_two;
static int num;
int main(int argc, char *argv[])
{
pthread_t id_t1, id_t2;
sem_init(&sem_one, 0, 0);
sem_init(&sem_two, 0, 1);
pthread_create(&id_t1, NULL, read, NULL);
pthread_create(&id_t2, NULL, accu, NULL);
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
sem_destroy(&sem_one);
sem_destroy(&sem_two);
return 0;
}
void* read(void* arg)
{
int i;
for(i=0; i<5; i++)
{
fputs("Input num: ", stdout);
sem_wait(&sem_two);
scanf("%d", &num);
sem_post(&sem_one);
}
return NULL;
}
void* accu(void* arg)
{
int sum=0, i;
for(i=0; i<5; i++)
{
sem_wait(&sem_one);
sum+=num;
sem_post(&sem_two);
}
printf("Result: %d \n", sum);
return NULL;
}
쓰레드 소멸
pthread_join() : 쓰레드 소멸해주지만, 종료될 때까지 블로킹됨pthread_detach() : 종료되지 않은 쓰레드 종료되지도 않고, 블로킹되지도 않음.int clnt_cnt=0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
int clnt_adr_sz;
pthread_t t_id;
if(argc!=2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
pthread_mutex_init(&mutx, NULL);
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
while(1)
{
clnt_adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
pthread_mutex_lock(&mutx);
clnt_socks[clnt_cnt++]=clnt_sock;
pthread_mutex_unlock(&mutx);
pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock);
pthread_detach(t_id);
printf("Connected client IP: %s", inet_ntoa(clnt_adr.sin_addr));
}
close(serv_sock);
return 0;
}
void* handle_clnt(void* arg)
{
int clnt_sock=*((int*)arg);
int str_len=0, i;
char msg[BUF_SIZE];
while((str_len=read(clnt_sock, msg, sizeof(msg)))!=0)
send_msg(msg, str_len);
pthread_mutex_lock(&mutx);
for(i=0; i<clnt_cnt; i++) // remove disconnected client
{
if(clnt_sock == clnt_socks[i])
{
while(i++<clnt_cnt-1)
clnt_socks[i]=clnt_socks[i+1];
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutx);
close(clnt_sock);
return NULL;
}
void send_msg(char* msg, int len) // send to all
{
int i;
pthread_mutex_lock(&mutx);
for(i=0; i<clnt_cnt; i++)
write(clnt_socks[i], msg, len);
pthread_mutex_unlock(&mutx);
}
chat 서버
int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in serv_addr;
pthread_t snd_thread, rcv_thread;
void* thread_return;
if(argc!=4) {
printf("Usage : %s <IP> <port> <name>\n", argv[0]);
exit(1);
}
sprintf(name, "[%s]", argv[3]);
sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("connect() error");
pthread_create(&snd_thread, NULL, send_msg, (void*)&sock);
pthread_create(&rcv_thread, NULL, recv_msg, (void*)&sock);
pthread_join(snd_thread, &thread_return);
pthread_join(rcv_thread, &thread_return);
close(sock);
return 0;
}
void* send_msg(void* arg)
{
int sock=*((int*)arg);
char name_msg[NAME_SIZE+BUF_SIZE];
while(1)
{
fgets(msg, BUF_SIZE, stdin);
if(!strcmp(msg,"q\n")||!strcmp(msg,"Q\n"))
{
close(sock);
exit(0);
}
sprintf(name_msg,"%s %s",name,msg);
write(sock, name_msg, strlen(name_msg));
}
return NULL;
}
void* recv_msg(void* arg)
{
int sock=*((int*)arg);
char name_msg[NAME_SIZE+BUF_SIZE];
int str_len;
while(1)
{
str_len=read(sock, name_msg, NAME_SIZE+BUF_SIZE-1);
if(str_len==-1)
return (void*)-1;
name_msg[str_len]=0;
fputs(name_msg, stdout);
}
return NULL;
}

Part 03 윈도우즈 기반 프로그래밍
운영체제가 만드는 리소스 : 프로세스, 쓰레드, 파일, 세마포어, 뮤텍스 등
커널 오브젝트
프로그램이 시작될 때 main 함수를 호출하는 주체는 쓰레드
프로세스는 자원/주소 공간, 쓰레드는 실행 흐름
윈도우 쓰레드 소멸 시점은 쓰레드가 맨 처음 호출된 main 함수가 반환하는 시점
CreateThread()
_beginthreadex() 쓰면 해결됨int main(int argc, char* argv[])
{
HANDLE hThread;
unsigned threadID;
int param = 5;
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)¶m, 0, &threadID);
if (hThread == 0)
{
puts("_beginthreadex() error");
return -1;
}
Sleep(3000);
puts("end of main");
return 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;
}
_beginthreadex()의 반환 값은 원래 unsinged인데, 역시 정수형인 HANDLE로 저장_beginthreadex()가 요구하는 호출규약 지키기 위해 WINAPI 삽입쓰레드를 구분하는 값에는 ID도 있고 핸들도 있다
메인 쓰레드 소멸하면 생성된 쓰레드들 다 사라져서, 5개가 아니라 3개만 호출되고 끝난다.
signaled 상태 : 종료됨
non-signaled 상태 : 안 종료됨
프로세스나 쓰레드 종료되면 해당 커널 오브젝트는 signaled 상태로 변경됨
WaitForSingleObject()
WaitForMultipleObjects()
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)¶m, 0, &threadID);
if (hThread == 0)
{
puts("_beginthreadex() error");
return -1;
}
if((wr=WaitForSingleObject(hThread, INFINITE))==WAIT_FAILED)
{
puts("thread wait error");
return -1;
}
printf("wait result: %s \n", (wr == WAIT_OBJECT_0) ? "signaled" : "time-out");
쓰레드 기다려주면 5개 다 출력된다