Unix | pthread

Jul 7, 2017


이 포스트는 Udacity - Introduction to Operating Systems를 강의 내용을 필기 형식으로 작성 했습니다.

What is Pthread ?

POSIX 스레드(POSIX Threads, 약어: PThread)는 병렬적으로 작동하는 소프트웨어의 작성을 위해서 제공되는 표준 API다.


Thread struct

pthread_t aThread; // type of Thread

pthread Attributes (pthread_attr_t)

  • pthread_create 안에 명시 하여 pthread 설정하도록 함
  • pthread_create 안에 NULL로 보낼시 default 값으로 설정 됨
  • 새로운 쓰레드에 특징을 명시 한다 특징들:
    • stack size
    • inheritance
    • joinable
    • scheduling policy
    • priority
    • system / process scope
// initialize pthread attribute structure 
int pthread_attr_init(pthread_attr_t *attr);  
// destroy and free pthread attribute structure from memory
int pthread_attr_destroy(pthread_attr_t *attr);  
// set or get value. 
//ex) int pthread_attr_{set/get}{attribute}  
int pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);  
// detach 관련 설정 
int pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);           
// scope 관련 설정, resource sharing scope 를 system scope로 변경
// (system 모든 thread 자원을 균등하게 사용 하겠다는 설정)

Thread createtion

int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg)); 
 //return 값은 create 함수 실행 결과

Thread Join

int pthread_join(pthread_t thread, void **status); 
// void **status <- 연관된 Thread 상태 값 반환 
// return 값은 join 함수 실행 결과

Compiling PThread

1) include header file in main file.

#include <pthread.h>

2) Compile source with -lpthread or pthread

gcc -o main main.c -lpthread
gcc -o main main.c -pthread

Detaching PThread

Default: joinable threads

  • Detach 하지 않고 부모 thread 가 자식thread 보다 먼저 exit 하면 자식thread는 좀비가 됨.
  • Detach 시키면 부모thread 가 exit 해도 자식thread는 계속 동작
  • 한번 Detach되면 다시 join할 수 없음
 
int pthread_detach();
pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACH);
// ... 
pthread_create(...., attr, ....);
void phread_exit();

PThread Mutexes

“to solve mutual exclusion problems among concurrent trheads”

mutex 구조체

 
pthread_mutex_t aMutex; // mutex type
int pthread_mutex_lock(pthread_mutex_t *mutex);  // explicit lock
int pthread_mutex_unlock(pthread_mutex_t *mutex);  // explicit unlock

사용 예)

 
list<int> my_list; 
pthread_mutex_t m; 
void safe_insert(int i) {
    pthread_mutex_lock(m);
    my_list.insert(i);
    pthread_mutex_unlock(m);
}

other mutex operation

 
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
 // mutex attribute : 프로세스 사이에서 공유될때 mutex 행동 설정 
int pthread_mutex_trylock(pthread_mutex_t *mutex); 
 // mutex lock 가능한지 확인 하는 기능 
int pthread_mutex_destroy(pthread_mutex_t *mutex); 
 // mutex 삭제
... many othrers ...

pthread condition variable

 
 - condition 
  ex) pthread_cond_t aCond;  // type of cond variable
 - wait
 : 기다리다가 mutex release되면 mutex획득 
  ex) int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
 - signal
 : wait 중인 쓰레드에게 신호 보냄
  ex) int pthread_cond_signal(pthread_cond_t *cond);
 - broadcast
 : 전체 쓰레드에 신호 보냄 
  ex) int pthread_cond_broadcast(pthread_cond_t *cond);

Other condition variable operations

 
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
// attributes -- e.g., if it's shared
// attr = null  // default behavior
// further specifiy behavior 
int pthread_cond_destroy(pthread_cond_t *cond);
// condition variabl 제거 


Create pthread without parameter

/* PThread Creation Quiz 1 */ 

#include <stdio.h>
#include <pthread.h>

#define NUM_THREADS 4

void *hello (void *arg) { /* thread main */
    printf("Hello Thread\n");
    return 0;
}

int main (void) {
    int i;
    pthread_t tid[NUM_THREADS];
    
    for (i = 0; i < NUM_THREADS; i++) { /* create/fork threads */
        pthread_create(&tid[i], NULL, hello, NULL);  // Null <- without parameter
    }
    
    for (i = 0; i < NUM_THREADS; i++) { /* wait/join threads */
        pthread_join(tid[i], NULL);
    }
    return 0;
}
/*
output result:
Hello Thread
Hello Thread
Hello Thread
Hello Thread
*/


Create pthread with parameter

/* PThread Creation Quiz 3 */ 

#include <stdio.h>
#include <pthread.h>

#define NUM_THREADS 4

void *threadFunc(void *pArg) { /* thread main */
    int myNum = *((int*)pArg);
    printf("Thread number %d\n", myNum);
    return 0;
}

int main(void) {

    int i;
    int tNum[NUM_THREADS];
    pthread_t tid[NUM_THREADS]; // pthread_t 생성 
    
    for(i = 0; i < NUM_THREADS; i++) { /* create/fork threads */
        tNum[i] = i;  // paramter 넘기기 
        pthread_create(&tid[i], NULL, threadFunc, &tNum[i]); // <--  &tNum[i] is parameter
        // pthread create 
    }
    
    for(i = 0; i < NUM_THREADS; i++) { /* wait/join threads */
        pthread_join(tid[i], NULL);
    }

    return 0;
}
/*
output result:
Thread number 0
Thread number 1
Thread number 2
Thread number 3
*/


Producer & Consumer

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BUF_SIZE 3        /* Size of shared buffer */

int buffer[BUF_SIZE];      /* shared buffer */
int add = 0;              /* place to add next element */
int rem = 0;              /* place to remove next element */
int num = 0;              /* number elements in buffer */

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;      /* mutex lock for buffer */
pthread_cond_t c_cons = PTHREAD_COND_INITIALIZER; /* consumer waits on this cond var */
pthread_cond_t c_prod = PTHREAD_COND_INITIALIZER; /* producer waits on this cond var */

void *producer (void *param);
void *consumer (void *param);

int main(int argc, char *argv[]) {

    pthread_t tid1, tid2;  /* thread identifiers */
    int i;

    /* create the threads; may be any number, in general */
    if(pthread_create(&tid1, NULL, producer, NULL) != 0) {
        fprintf(stderr, "Unable to create producer thread\n");
        exit(1);
    }

    if(pthread_create(&tid2, NULL, consumer, NULL) != 0) {
        fprintf(stderr, "Unable to create consumer thread\n");
        exit(1);
    }

    /* wait for created thread to exit */
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("Parent quiting\n");

    return 0;
}

/* Produce value(s) */
void *producer(void *param) {

    int i;
    for (i=1; i<=20; i++) {
        
        /* Insert into buffer */
        pthread_mutex_lock (&m);    
            if (num > BUF_SIZE) {
                exit(1);  /* overflow */
            }

            while (num == BUF_SIZE) {  /* block if buffer is full */
                pthread_cond_wait (&c_prod, &m);
            }
            
            /* if executing here, buffer not full so add element */
            buffer[add] = i;
            add = (add+1) % BUF_SIZE;
            num++;
        pthread_mutex_unlock (&m);

        pthread_cond_signal (&c_cons);
        printf ("producer: inserted %d\n", i);
        fflush (stdout);
    }

    printf("producer quiting\n");
    fflush(stdout);
    return 0;
}

/* Consume value(s); Note the consumer never terminates */
void *consumer(void *param) {

    int i;

    while(1) {

        pthread_mutex_lock (&m);
            if (num < 0) {
                exit(1);
            } /* underflow */

            while (num == 0) {  /* block if buffer empty */
                pthread_cond_wait (&c_cons, &m);
            }

            /* if executing here, buffer not empty so remove element */
            i = buffer[rem];
            rem = (rem+1) % BUF_SIZE;
            num--;
        pthread_mutex_unlock (&m);

        pthread_cond_signal (&c_prod);
        printf ("Consume value %d\n", i);  fflush(stdout);

    }
    return 0;
}

/*
output result: 
producer: inserted 1
Consume value 1
producer: inserted 2
Consume value 2
producer: inserted 3
Consume value 3
... 
...
producer: inserted 18
Consume value 18
producer: inserted 19
Consume value 19
producer: inserted 20
Consume value 20
producer quiting
프로세스는 종료 되지 않은 상태로 유지 (이유: consumer가 무한 루프이고 main에 join되어 있기 때문)
*/