16.스레드

 #.프로세스와 스레드

앞에서 Fork() 함수로 프로세스를 복사하는 법을 다루어보았는데. 이방법은 자원의 효율성이 떨어진다. 

프로세스의 code, data, stack, file I/o, signal table 의 정보가 모두 복사되기 때문이다. 코드의 일부만 병렬로 실행시키고 싶을때는 낭비가 아닐 수 없다.그래서 스레드의 개념이 나왔다.

 

스레드는 한프로세스안에서 자원을 공유하면서 빠르게, 효율적으로 작업을 처리 할 수 있다. 프로세스는 독립적인자원을 가지는 반면에 스레드는 stat, memory등 자원을 공유한다. 그리고 프로세스는 자신만의 주소공간을 가지지만 스레드는 주소공간을 공유한다. 프로세스는 IPC를 이용해야만 통신이 가능하다. 그리고 스레드의 문맥교환은 프로세스의 문맥교환 보다 빠르다. 하지만 스레드는 하나의 스레드에 문제가 발생하면 프로세스 전체에 영향을 줄 수 있다는 단점이 있다.

 

스레드는 소스에서 처음 시작될때 main 함수에서 시작되는 단일 쓰레드 상태로 작동이 된다. 여기서 pthread_create(3) 함수를 호출하면 새로운 스레드가 생성된다. 부모 스레드는 pthread_join() 함수를 이용해서 자식 스레드의 종료를 기다린다. 자식 스레드가 종료되면 부모는 자원을 정리하는 일을 한다.

 

 #include <pthread.h>

 int  pthread_create(pthread_t  *  thread, pthread_attr_t *attr,

      void * (*start_routine)(void *), void * arg);

 

 -thread : 쓰레드가 성공적으로 생성되었을 때, 넘겨주는 쓰레드 식별 번호.

 -attr : 쓰레드의 특성을 설정하기 위해서 사용한다. NULL일 경우 기본 특성

 -start_routine : 쓰레드가 수행할 함수로 함수포인터를 넘겨준다.

 -arg : 쓰레드 함수 start_routine를 실행시킬 때, 넘겨줄 인자

 

 이 함수는 성공적으로 수행되었다면, 0을 리턴한다. 그렇지 않을 경우 1을 리턴한다.

 

 #include <pthread.h>

 int pthread_join(pthread_t th, void **thread_return);

 

- th : pthread_create에 의해서 생성된, 식별번호 th를 가진 쓰레드를 기다리겠다는 얘기다.

- thread_return : 식별번호 th인 쓰레드의 종료시 리턴값이다.

 

예제

결과

스레드 2개가 동시에 번갈아 가면서 실행된다.

 

 #.위의 소스의 문제점

pthread_join은 쓰레드를 두개 이상 생성시키지 못한다.

pthread_join은 쓰레드가 종료될 때까지 블럭되기 때문이다.

pthread_join을 이용하지 않는다면, 메모리 누수가 생긴다...

 

#방법

pthread_detach 함수 사용.

자식 스레드를 부모쓰레드와 완전히 분리 시킨다.

이때 자식 쓰레드가 종료되면, 모든 자원이 즉시 반환 된다.

만약 자식 스레드의 종료상태를 알아내는게 중요하다면, 종료상태를 저장할 전역변수를 두고,

여기에 종료상태를 기록하는 방식을 사용할 수 도 있다.

 

예제

결과

 

 

 #.임계영역

 

 동시에 여러개의 쓰레드가 하나의 자원에 접근하려고 할때 문제가 발생한다. pthread는 임계영역을 두는 방법을 사용한다. pthread는  mutex를 제공한다.

 

* thread1이 자원에 접근 (잠금획득)

 

* thread2 임계영역 밖에서 대기

 

* thread1이 자원 모두 사용 종료 (잠금반납)

 

* thread2 자원에 접근 (잠금획득)

 

 #.mutex를 사용하기 위해서는 다음의 4가지 함수가 필요하다.

 1..mutex 잠금객체을 만드는 함수

 2.mutex 잠금을 얻는 함수

 3.mutex 잠금을 되돌려주는 함수

 4.mutex 잠금객체를 제거하는 함수

 

1.pthread_mutex_init

*mutex를 사용하기 위해서는 먼저 pthread_mutex_init() 함수를 이용해서, mutex 잠금 객체를 만들어줘야 한다.

 

pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr *attr);

 

 -mutex : mutex 잠금객체

 -mutex_attr : mutex는 fast, 'recursive, error checking의 3종류가 있다.

 

 이 값을 이용해서 mutex 타입을 결정할 수 있다. NULL 일경우 기본값이 fast가 설정된다.

 fast : 하나의 쓰레드가 하나의 잠금만을 얻을 수 있는 일반적인 형태

 recursive : 잠금을 얻은 쓰레드가 다시 잠금을 얻을 수 있다. 이 경우 잠금에 대한 카운드가 증가하게 된다.

 mutex_attr을 위해서 다음의 상수값이 예약되어 있다.

 fast : PTHREAD_MUTEX_INITIALIZER

 recursive : PTHREAD_RECURSIVE_MUTEX_INITIALIZER

 error checking : PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP

 

 2.pthread_mutex_lock

*mutex 잠금을 얻기 위한 함수다.

int pthread_mutex_lock(pthread_mu);

mutex 잠금을 얻는다라는 표현보다는 mutex 잠금을 요청한다라는 표현이 더 정확할 수 있다.

만약 mutex 잠금을 선점한 쓰레드가 있다면, 선점한 쓰레드가 mutex 잠금을 되돌려주기 전까지 이 코드에서 대기하게 된다.

때때로 잠금을 얻을 수 있는지만 체크하고 대기(블럭)되지 않은 상태로 다음 코드로 넘어가야할 필요가 있을 수 있을 것이다.

이 경우에는 아래의 함수를 사용하면 된다.

int pthread_mutex_trylock(pthread_mutex_t *mutex);

 

 3.pthread_mutex_unlock

 *mutex 잠금을 되돌려주는 함수다.

 int pthread_mutex_unlock(pthread_mutex_t *mutex);

 

예제

결과

do_loop2 함수에 진입을 했지만, do_loop1에서 잠금을 요청하고,  임계구역에 접근하고 있으므로  do_loop2 는 기다린다.

 

 

 

 

출처,참고 : joinC