Thread란?

  • Process 내부에 있는 single sequence stream이다.
  • Process의 특징을 가지고 있기 때문에 lightweight process라고 불리기도 한다.

Thread와 Process의 차이

  • Thread는 완전히 independent하지 않다.
  • 다른 threads의 resource를 나눠쓴다.(code section, data section, resources)
  • 하지만 여전히 개별 PC(program counter), register set, stack space를 가지고 있다.

왜 Multithreading을 하는가?

  • 병렬구조를 사용하여 application을 향상시킨다.
  • 예를 들어, window explorer의 여러개의 탭도 thread의 일부로 볼 수 있다.
  • Thread는 process보다 훨씬 속도가 빠르다.

Thread가 Process보다 빠른 이유

  1. 생성이 빠르다.
  2. 쓰레드 간에 Context switching이 빠르다
  3. 쓰레드는 쉽게 종료된다.
  4. 쓰레드 간의 소통이 빠르다.

Threads in C

  • C는 자바처럼 멀티쓰레딩을 지원하지 않는다.
  • 그래서 pthread라는 POSIX standard for threads를 사용한다.

PThread Functions

pthread_create

Thread를 생성한다.

pthread_create(&thread_id, NULL, myThreadFun, NULL);

Arguments

  • first : thread_t 변수의 주소값
  • second : thread_t의 attribute
  • third : 쓰레드가 실행시킬 함수
  • fourth : 함수에게 전달할 파라미터

pthread_join

pthread_join(thread_id, NULL); 

thread_id로 전달된 쓰레드가 종료되는 것까지 기다린다.

pthread_mutex_lock

int pthread_mutex_lock(pthread_mutex_t *mutex);

이 함수가 호출되면 호출한 thread가 mutex가 열릴 때가지 막는다.

pthread_mutex_unlock

int pthread_mutex_unlock(pthread_mutex_t *mutex); 

막아져 있던 thread를 풀어준다.

pthread_cond_wait

pthread_cond_wait(&cond, &mutex);

시그널이 올 때까지 sleep하고 있다가 시그널이 오면 잠깐 mutex를 unlock한다.

pthread_cond_signal

pthread_cond_signal(&cond);

condition variable을 통해서 시그널을 보낸다.

main()

int main() {
  pthread_t thread1, thread2;
  
  char *message1 = "Thread 1";
  char *message2 = "Thread 2";
  
  pthread_create( &thread1, NULL, print_message_function1, (void*) message1);
  pthread_create( &thread2, NULL, print_message_function2, (void*) message2);
  
  pthread_join( thread1, NULL);
  pthread_join( thread2, NULL); 
  
  exit(0);
}
  • line 2: pthread_t 라는 타입의 thread를 두개 생성해준다.
  • line 7, 8: pthread_create 함수를 통해 thread를 생성해준다.
  • line 10, 11: thread가 죽을 때까지 기다린다.

lock, signal, condition 활용 thread communication

  • 각각 데이터를 3개씩 번갈아 가면서 출력해보자.
  • 문제는 thread는 동시에 parallel하게 진행되기 때문에 순차적으로 진행되지 않는다.
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER ;
pthread_cond_t  c1 = PTHREAD_COND_INITIALIZER ;
pthread_cond_t  c2 = PTHREAD_COND_INITIALIZER ;

void *print_message_function1( void *ptr ){
  char *message;
  message = (char *) ptr;
  
  int i = 0 ;
  for (i = 0 ; i < 3 ; i++)  {
    printf("%s\n", message) ;
    printf("%s\n", message) ;
    printf("%s\n", message) ;
    
    pthread_mutex_lock(&m) ;
      pthread_cond_signal(&c2) ;
    pthread_mutex_unlock(&m) ;
    
    pthread_mutex_lock(&m) ;
      pthread_cond_wait(&c1, &m) ;
    pthread_mutex_unlock(&m) ;
  }
}

void *print_message_function2( void *ptr ){
  char *message;
  message = (char *) ptr;
  
  int i = 0 ;
  for (i = 0 ; i < 3 ; i++)  {
    pthread_mutex_lock(&m) ; // racy
      pthread_cond_wait(&c2, &m) ;
    pthread_mutex_unlock(&m) ;
    
    printf("%s\n", message) ;
    printf("%s\n", message) ;
    printf("%s\n", message) ;
    
    pthread_mutex_lock(&m) ;
      pthread_cond_signal(&c1) ;
    pthread_mutex_unlock(&m) ;
  }
}
  • Line 16, 32: 1번 함수 출력이 끝나면 c2라는 condition변수를 이용해서 signal을 보내고 2번 함수 thread를 unlock해준다.
  • Line 40, 20: 2번 함수 출력이 끝나면 c1이라는 condition 변수를 이용해서 1번함수에 signal을 보내고 1번 함수의 20번째 줄부터 unlock해준다.

이렇게 서로 시그널을 주고 받으면서 순차적으로 출력을 마무리한다.

결과 확인

pthread는 compile시 -pthread 옵션을 추가해준다.

jeongjinhyeog-ui-MacBook-Pro:Pthread jeongjinhyeog$ gcc -pthread pthread4.c
jeongjinhyeog-ui-MacBook-Pro:Pthread jeongjinhyeog$ ./a.out 
Thread 1
Thread 1
Thread 1
Thread 2
Thread 2
Thread 2
Thread 1
Thread 1
Thread 1
Thread 2
Thread 2
Thread 2

보는 것과 같이 결과값이 각 함수당 3개씩 순차적으로 출력되는 것을 볼 수 있다!

느낀점

  • thread는 정말 필요할 것 같다.
  • thread와 process를 여러개 사용하는 차이점을 확실히 알아둬야 나중에 내가 뭘 써야할지 알 수 있을 것 같다.