Intro to Race Conditions
How can I create ten threads with different starting values.
#include <pthread.h> void* myfunc(void* ptr) { int i = *((int *) ptr); printf("%d ", i); return NULL; } int main() { // Each thread gets a different value of i to process int i; pthread_t tid; for(i =0; i < 10; i++) { pthread_create(&tid, NULL, myfunc, &i); // ERROR } pthread_exit(NULL); }
위 프로그램은 race condition 상태입니다. i의 값이 계속 바뀌고 있습니다. 새로운 쓰레드는 나중에 시작합니다.(위의 예에서 마지막 쓰레드의 output은 루프가 끝난 다음에 시작합니다.)
이러한 race condition을 극복하려면 각각의 쓰레드에게 각자 데이터 영역에 포인터를 주어야 합니다.
예를 들면, 각각의 쓰레드는 id, starting value, output value를 저장하기 원할수도 있습니다.
struct T { pthread_t id; int start; char result[100]; };
이것들은 malloc으로 heap에 할당된 array에 저장될 수 있습니다.
struct T *info = calloc(10 , sizeof(struct T)); // reserve enough bytes for ten T structures
그 후에 각각의 element가 쓰레드에 전달됩니다.
pthread_create(&info[i].id, NULL, func, &info[i]);
Why are some functions e.g asctime, getenv, strtok, strerror not thread-safe?
이것에 대한 답을 하기 위해 thread-safe가 아닌 단순한 함수를 살펴봅시다.
char *to_message(int num) { char static result [256]; if (num < 10) sprintf(result, "%d : blah blah" , num); else strcpy(result, "Unknown"); return result; }
위의 코드에서 result buffer는 global memory에 저장되어 있습니다. 이렇게 함으로써 스택의 유효하지 않은 주소를 반환할 일은 없어졌지만 전체 메모리에는 하나의 result buffer만 존재하게 됩니다. 만약 두개의 쓰레드가 동시에 이 버퍼를 사용한다면 한 쓰레드는 다른 쓰레드를 오염시킬것입니다.
Time | Thread 1 | Thread 2 | Comments |
---|---|---|---|
1 | to_m(5 ) | ||
2 | to_m(99) | Now both threads will see "Unknown" stored in the result buffer |
What are condition variables, semaphores, mutexes?
Are there any advantages of using threads over forking processes?
Are there any dis-advantages of using threads over forking processes?
Isolation이 없다는 것입니다. 쓰레드들은 같은 프로세스 안에 있기 때문에 하나의 쓰레드가 다른 쓰레드도 사용하는 가상 메모리에 접근할 수 있습니다. 하나의 쓰레드가 전체 프로세스를 종료시킬 수도 있습니다(0번지를 읽기 등).
Can you fork a process with multiple threads?
#include <pthread.h> #include <stdio.h> #include <unistd.h> static pid_t child = -2; void *sleepnprint(void *arg) { printf("%d:%s starting up...\n", getpid(), (char *) arg); while (child == -2) {sleep(1);} /* Later we will use condition variables */ printf("%d:%s finishing...\n",getpid(), (char*)arg); return NULL; } int main() { pthread_t tid1, tid2; pthread_create(&tid1,NULL, sleepnprint, "New Thread One"); pthread_create(&tid2,NULL, sleepnprint, "New Thread Two"); child = fork(); printf("%d:%s\n",getpid(), "fork()ing complete"); sleep(3); printf("%d:%s\n",getpid(), "Main thread finished"); pthread_exit(NULL); return 0; /* Never executes */ }
사실 forking 전에 Thread들을 생성하는 것은 forking할 때 다른 Thread들이 즉시 terminated되므로 위에서 보였듯이 예측되지 않는 에러를 일으킬 수 있습니다. 다른 쓰레드는 mutax를 lock한 후에(malloc을 호출해서) 다시는 unlock하지 않습니다. 고급 사용자들은 pthread_atfork를 찾아 유용하게 사용할 수도 있지만, 위와 같은 접근의 한계와 어려움을 완전히 이해하기 전까지 forking 전에 쓰레드를 생성하는 일은 피하는게 좋다고 권장합니다.
Are there other reasons where fork might be preferable to crating a thread.
- security를 보장하고 싶을 때(예를 들면 chrome은 탭마다 다른 프로세스를 사용합니다)
- 기존의 완전한 프로그램을 실행할 때 새로운 프로세스가 필요한 경우(starting GCC)
- synchronization primitives로 동작 중이고 각각의 프로세스가 시스템의 다른 곳에서 작동 중일때
'Angrave System Programming > Intro to Pthreads' 카테고리의 다른 글
pthreads : Parallel Problems(Bonus) (0) | 2019.01.14 |
---|---|
Pthreads : Usage in Practice(1) (1) | 2019.01.14 |
Pthread Introduction (0) | 2019.01.14 |