Single-thread vs Multi-thread
- code, data, file 데이터는 여러개의 스레드에서 공유되는 자원이다.
- register, stack 영역의 데이터는 각 스레드마다 독립적으로 사용된다.
- Single-thread로 사용하는 것 보다 Multi-thread가 성능이 훨씬 좋다.
- 다만 Multi-thread를 사용할때 각 스레드가 사용되는 공유 자원 관리가 매우 중요하다. (critical section)
사용 헤더
- pthread.h
- makefile: CFLAGS에 -pthread 옵션 필수
사용 함수
- pthread_create : 스레드 생성해주는 함수
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
파라미터
- thread: 생성된 thread ID
- attr: 쓰레드 속성(pthread_attr_init()으로 초기화)
- start_routine: thread main function
- arg: thread main function 호출 시 사용할 파라미터
반환값
- 성공: 0
- 실패: errno를 리턴
- pthread_exit: 스레드 종료 함수
void pthread_exit(void *retval);
파라미터
- retval: exit status를 저장
- pthread_join: 스레드 종료를 기다리는 함수
int pthread_join(pthread_t thread, void **retval);
파라미터
- thread: 기다릴 thread ID
- retval: 해당 thread의 exit status를 저장
반환값
- 성공시0
- 실패시 errno를 리턴
- pthread_detach: pthread_join을 사용하지 않더라도, ㅅ레드 종료될때 모든 자원을 해제한다.
int pthread_detach(pthread_t thread);
파라미터
- thread: 떼어낼 thread ID
반환값
- 성공시0
- 실패시 errno를 리턴
- pthread_mutex_init: mutex 객체 초기화
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
파라미터
- mutex: mutex instance
- attr: mutex 속성
반환값
- 성공시0
- 실패시 errno를 리턴
- pthread_mutex_destroy: mutex 객체 free
int pthread_mutex_destroy(pthread_mutex_t *mutex);
파라미터
- mutex: mutex instance
반환값
- 성공시0
- 실패시 errno를 리턴
- pthread_mutex_lock, trylock, unlock
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
파라미터
- mutex: mutex instance
반환값
- 성공시0
- 실패시 errno를 리턴
예제 코드
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#define MAX_NUM 100
#define PLUS_NUM 10
#define FILE_PATH "pthread_debug.txt"
pthread_mutex_t share_memory_control;
int share_memory = 0;
void *count_share_memory(void *arg)
{
int fd = 0;
for(int i=0; i<MAX_NUM; i++){
pthread_mutex_lock(&share_memory_control);
fd = open((const char *)FILE_PATH, O_CREAT | O_APPEND | O_RDWR, 0644);
if(fd == -1){
printf("open() fail: %s\n", strerror(errno));
exit(0);
}
dprintf(fd, "[Child ID: %lu] share_memory: %d\n", pthread_self(), share_memory);
close(fd);
share_memory++;
sleep(1);
pthread_mutex_unlock(&share_memory_control);
}
}
void *plus_share_memory(void *arg)
{
int fd = 0;
for(int i=0; i<10; i++){
pthread_mutex_lock(&share_memory_control);
fd = open((const char *)FILE_PATH, O_CREAT | O_APPEND | O_RDWR, 0644);
if(fd == -1){
printf("open() fail: %s\n", strerror(errno));
exit(0);
}
dprintf(fd, "[Child ID: %lu] share_memory: %d\n", pthread_self(), share_memory);
share_memory += (int)PLUS_NUM;
close(fd);
pthread_mutex_unlock(&share_memory_control);
sleep(3);
}
}
int main(int argc, char **argv)
{
pthread_t pthread_list[2];
int ret = 0;
if(pthread_mutex_init(&share_memory_control, NULL)){
printf("pthread_mutex_init fail: %s\n", strerror(errno));
return -1;
}
printf("[MAIN] Thread id: %lu\n", pthread_self());
ret = pthread_create(&pthread_list[0], NULL, count_share_memory, NULL);
if(ret){
printf("pthread_create fail: %s\n", strerror(errno));
return -1;
}
ret = pthread_create(&pthread_list[1], NULL, plus_share_memory, NULL);
if(ret){
printf("pthread_create fail: %s\n", strerror(errno));
return -1;
}
printf("trying to join %lu\n", pthread_list[0]);
if(pthread_join(pthread_list[0], NULL)){
printf("pthread_join(%lu) fail\n",pthread_list[0]);
}
printf("trying to join %lu\n", pthread_list[1]);
if(pthread_join(pthread_list[1], NULL)){
printf("pthread_join(%lu) fail\n",pthread_list[0]);
}
if(pthread_detach(pthread_list[0]) != 0){
printf("pthread_detach 0 fail: %s\n", strerror(errno));
return -1;
}
if(pthread_detach(pthread_list[1]) != 0){
printf("pthread_detach 1 fail: %s\n", strerror(errno));
return -1;
}
if(pthread_mutex_destroy(&share_memory_control) != 0){
printf("pthread_mutex_destroy fail: %s\n", strerror(errno));
return -1;
}
return 0;
}