수정입니다
27. Interlude : Thread API 본문
Thread Creation
#include <pthread.h>
pthread_t thread; // 미리 thread 변수 생성
...
int
pthread_create(
pthread_t * thread // 변수 주소값 넘겨서 기록
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg // void pointer type => argument에 아무 type이나 올 수 있게 해줌
);
/*example*/
// integer argument
void* (*start_routine)(int),
int arg
// return an integer
int (*start_routine)(void*)
void* arg
- attr : 여러 속성 기록(보통 null)
- start_routine : 이 함수 기준으로 thread가 시작 & 끝
- arg : start_routine에 넘겨질 argument
Example : Creating a Thread
#include <pthread.h>
typedef struct __myarg_t {
int a;
int b;
} myarg_t;
void* mythread(void* arg) {
myarg_t* m = (myarg_t*)arg;
printf("%d %d\n", m->a, m->b);
return NULL;
}
int main(int argc, char* argv[]) {
pthread_t p;
int rc; // thread가 잘 생성 됐는지 / 아닌지 판별
myarg_t args;
args.a = 10;
args.b = 20;
rc = pthread_create(&p, NULL, mythread, &args);
}
- mythread() 함수가 끝나면 thread가 죽음 => 죽게 하고 싶지 않다면 while(flag)로 제어 가능
- mythread 내에서 arg의 주소를 받아 myarg_t 형으로 변환 => 멤버 변수 출력 == 10, 20 출력
Wait for a thread to complete
void *value_ptr; // return 값의 포인터를 받을 변수
...
int pthread_join(pthread_t thread, void **value_ptr);
// void **valule_ptr => thread의 함수 실행이 끝난 return 값의 포인터를 포인터 형식으로 받음
- pthread_join : 특정 thread가 죽을 때까지 기다리는 함수, wait()랑 비슷
- argument로 value_ptr의 double pointer를 받아야 함
- => *value_ptr 이라는 >original pointer 변수 값<을 변경 가능하게 해줘야 하기 때문에, pass in value가 아닌 pass in pointer를 해줘야 하고, 때문에 포인터 변수 값의 포인터를 받아야함
- 즉, thread를 만든 process가 pthread_join을 같이 부르면, 그 thread가 어떤 함수를 실행 하고, 끝날 때 그 return 값의 pointer를 받아서 나중에 수정 같은걸 할 수 있게 해야하기 때문에 pass in pointer로 해줘야 된다는 얘기...
Waiting for Thread Completion
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
typedef struct __myarg_t {
int a;
int b;
} myarg_t;
typedef struct __myret_t {
int x;
int y;
} myret_t;
void* mythread(void* arg) {
myarg_t* m = (myarg_t*)arg;
printf("%d %d\n", m->a, m->b);
myret_t* r = malloc(sizeof(myret_t));
r->x = 1;
r->y = 2;
return (void *) r;
}
int main(int argc, char* argv[]) {
int rc;
pthread_t p;
myret_t* m; // 쓰레기값 담겨있음
myarg_t args;
args.a = 10;
args.b = 20;
rc = pthread_create(&p, NULL, mythread, &args);
pthread_join(p, (void**)&m);
printf("returned %d %d\n", m->x, m->y);
return 0;
}
- mythread() routine에서 arg로 받은 값 10, 20 출력 후
- myret_t 라는 구조체 변수 할당 => 구조체 pointer 로 return 하면 두개 이상의 값을 return 할 수 있는 장점이 있음
- 구조체의 멤버 변수에 값 할당 후, 구조체 시작 주소 return
- main 함수에서 그 구조체 변수의 시작 pointer를 가리키는 pointer에 return 값 pointer의 pointer가 오면, 원래 쓰레기 값이 담겨 있던 m에, return 값 주소를 담게 변경을 해줌
Example : Dangerous code
void* mythread(void* arg) {
myarg_t* m = (myarg_t*)arg;
printf("%d %d\n", m->a, m->b);
myret_t* r; // heap에 할당 안되고, stack에 할당 됨
r->x = 1;
r->y = 2;
return (void *) r;
}
- heap에 할당(malloc 사용) 하면, mythread가 끝나고 나서도, 계속 정보가 남아있는데, stack에 할당이 되면 mythread가 끝난 후 사라지거나, 아무튼 어떻게 될 지 모르기 때문에 끝나고 다시 r에 접근하면 잘못 된 값이 나올 수 있게 됨
Locks
- 항상 적절히 초기화 돼야 함
- static way : 매크로 사용
- dynamic way : assert(rc == 0) => 실행 중에 0으로 초기화
Compiling and Running
prompt> gcc -o main main.c -Wall -pthread
- 뒤에 -pthread를 붙여야 linking이 잘 된다고 함
'전공 > 운영체제' 카테고리의 다른 글
36. I/O Devices (1) | 2024.01.15 |
---|---|
28. Locks (1) | 2024.01.15 |
26. Concurrency : An Introduction (1) | 2024.01.15 |
20. Paging: Smaller Tables (1) | 2024.01.14 |
19. Translation Lookaside Buffers (1) | 2024.01.14 |