이 글은 윤성우의 TCP/IP 소켓 프로그래밍 책을 참고하였습니다.
순서
I. 스레드의 생성
pthread_create 함수
II. 프로세스와의 동기화
pthread_join함수
I. 스레드의 생성
함수를 먼저 보자.
#include <pthread.h>
int pthread_create( pthread_t *restrict thread, const pthread_attr_t *restrict attr,
void* (*start_routine)(void*), void *restrict arg);
--> 성공 시 0, 실패 시 0 이외의 값 반환
ㄴ. thread : 생성할 스레드의 ID 저장을 위한 변수의 주소 값 전달,
프로세스와 마찬가지로 스레드의 구분을 위한 ID가 부여된다.
ㄴ. attr : 스레드에 부여할 특성 정보의 전달을 위한 매개변수, NULL 전달 시 기본특성.
ㄴ. start_routine : 스레드의 main 함수 역할을 하는,
변도 실행흐름이 시작되는 함수의 주소값(함수 포인터) 전달.
ㄴ. arg : 세 번째 임자를 통해 등록된 함수가 호출될 때 전달할 인자의 주소전달
스레드를 사용하는 데에는 restrice와 함수 포인터 문법을 잘 알아야 하지만,
단순히 사용방법만 서술할 것이다.
인자1. pthread_t *restrict thread
프로세스를 생성할 때에도 fork()를 통해 PID를 얻은 바가 있다.
이처럼 스레드도 pthread_t의 자료형을 가지는 스레드의 ID가 필요한데,
이 변수를 미리 선언하여 인자로 넣어두면 된다.
인자2. const pthread_attr_t *restrict attr
NULL로 기본 특성 보내자.
인자3. start_routine
스레드도 하나의 별도 실행 흐름을 가진다. 우리는 이걸 함수로 만들 것이고
이 함수의 주소 값, 즉 함수 포인터를 전달하면 된다.
그러면 그 함수를 실행하게 되는 것이다.
인자4. arg
만약 함수 포인터로 전달한 그 메인 스레드 함수에 인자가 필요하다면,
미리 정의해서 그 주소를 보내야 한다.
이를 이용해서 구현하면
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void* thread_main(void *arg);
int main()
{
pthread_t thread_ID;
int thread_arg = 5;
if(pthread_create(&thread_ID, NULL, thread_main, (void*)&thread_arg) != 0)
{
puts("pthread_create() ERR!\n");
return -1;
}
sleep(10); puts("end of main\n");
return 0;
}
void* thread_main(void *arg)
{
int cnt = *((int*) arg); // void포인터 형을 int 포인터 형으로 캐스팅
for(int i=0;i<cnt;i++)
{
sleep(1);
puts("running thread %d\n",i);
}
return NULL;
}
실행 결과
running thread 0
running thread 1
running thread 2
running thread 3
running thread 4
end of main
특이한 점이 두 가지 있다.
1. main함수를 끝낼 때 sleep을 한 점
2. 인자를 void*형으로 보내는 것.
1.의 이유는 현재 우리가 pthread_create로만 구현을 했기 때문에
프로세스가 죽으면서 스레드도 같이 죽어 스레드 함수 내부를 충분히 실행을 못 시킬 수 있기 때문이다.
2.의 이유는 이식성 때문에 그럴 것이라고 생각한다.
함수 포인터를 사용할 때 void로 모든 형을 받아오고, 함수 내에서 필요한 자료형으로 캐스팅을 쓰는 것이
이식성도 좋고 범용적이지 않을까...?
II. 프로세스와의 동기화
앞서 우리는 프로세스가 끝날 때 스레드도 같이 종료되었다.
그렇다면, 스레드가 돌고 있는 동안에 프로세스를 잠시 대기시킬 순 없을까?
이 문제점을 pthread_join함수가 해결해준다.
#include <pthread.h>
int pthread_join(pthread_t thread, void **status);
--> 성공 시 0, 실패 시 0 이외의 값 반환
ㄴ. thread : thread ID로 이 매개변수의 ID를 가진 스레드가 종료하기 전까지 함수는 반환하지 않음
ㄴ. status : 스레드의 main 함수가 반환하는 값이 저장될 포인터 변수의 주소 값을 전달
인자1. pthread_t thread
pthread_create에 우리는 pthread_t의 자료형으로 선언된 변수의 주소 값을 인자로 넣었고,
그 결과로 그 변수 안에 thread ID가 저장되어 있다.
따라서 원하는 스레드의 ID변수를 넣어주면 된다.(주소 X)
이 ID를 가진 스레드가 끝나기 전까지 프로세스(또는 스레드)를 대기상태에 둔다.
인자2. void **status
함수의 반환이 끝나면 그 반환된 값을 저장할 포인터 변수의 주소 값을 전달한다.
중요한 점은 포인터 변수의 주소 값이다.
= 변수의 주소의 주소 값
그 이유는 우리는 함수 포인터로 함수를 정의하였기 때문에
반환 값이 값의 포인터 변수가 되기 때문이다.
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void* thread_main(void *arg);
int main()
{
pthread_t thread_ID;
int thread_arg = 5;
void* thr_ret;
if(pthread_create(&thread_ID, NULL, thread_main, (void*)&thread_arg) != 0)
{
puts("pthread_create() ERR!\n");
return -1;
}
if (pthread_join(thread_ID, &thr_ret) != 0)
{
puts("pthread_join() ERR!\n");
return -1;
}
puts("end of main, retmsg = %s",(char*)thr_ret);
return 0;
}
void* thread_main(void *arg)
{
int cnt = *((int*) arg); // void포인터 형을 int 포인터 형으로 캐스팅
char* retmsg = "Hello";
for(int i=0;i<cnt;i++)
{
sleep(1);
puts("running thread %d\n",i);
}
return (void*)retmsg;
}
실행 결과
running thread 0
running thread 1
running thread 2
running thread 3
running thread 4
end of main, retmsg = Hello
'Programming > Network(C++)' 카테고리의 다른 글
[Network][Thread] 멀티스레드(4)_스레드 동기화(Mutex, Semaphore)(Linux) (0) | 2020.12.21 |
---|---|
[Network][Thread] 멀티스레드(3)_동시접근의 문제점 (0) | 2020.12.21 |
[Network][Thread] 멀티스레드(1)_스레드의 이해 (0) | 2020.12.20 |
[Network][TCP/IP] 멀티 플렉싱(5)_레벨, 엣지 트리거(Only Linux) (0) | 2020.12.19 |