Crucial concept: Thread Safety
함수가 여러 Concurrent Thread에서 반복적으로 호출될 때 항상 올바른 결과를 생성하는 경우에만 Thread-safe라고 한다.
Thread-unsafe 함수의 Class
- Class 1: 공유 변수를 보호하지 않는 함수
- Class 2: 여러 호출에 걸쳐 상태를 유지하는 함수
- Class 3: static 변수의 pointer를 반환하는 함수
- Class 4: thread-unsafe 함수라 불리는 함수
Thread-Unsafe Functions (Class 1)
공유 변수를 보호하지 못한 함수
- FIx: P와 V Semapore 연산을 사용한다.
- Example: goocnt.c
- 문제점: 동기화 연산은 코드 실행 속도를 저하시킨다.
Thread-Unsafe Functions (Class 2)
여러 번 함수 호출하면서 상태를 유지하는 함수
Ex) Ran: Random number generator
static unsigned int next = 1;
/* rand: return pseudo-random integer on 0..32767 */
int rand(void) {
next = next * 1103515245 + 12345;
return (unsigned int)(next / 65536) % 32768;
}
/* srand: set seed for rand() */
void srand(unsigned int seed) {
next = seed;
}
여러 Thread에서 공유하는 next가 바뀌기 때문에 unsafe하다.
Thread-Safe Random Number Generator
최대한 Thread끼리 Global Variable이 발생하지 않게 만들기.
/* rand_r - return pseudo-random integer on 0..32767 */
int rand_r(int *nextp) {
*nextp = *nextp * 1103515245 + 12345;
return (unsigned int)(*nextp/65536) % 32768;
}
void로 인자를 넘기지 않고 지역 변수의 포인터로 넘겨준다. 인자로 상태를 넘긴다.
Thread-Unsafe Functions (Class 3)
/* lock-and-copy version */
char *ctime_ts(const time_t *timep,
char *privatep) {
char *sharedp;
P(&mutex);
sharedp = ctime(timep);
strcpy(privatep, sharedp);
V(&mutex);
return privatep;
}
해결책
statice variable의 pointer를 반환한다.
- caller가 결과를 저장할 수 있도록 변수의 주소를 넘긴다.
- Lock-and-copy
- lock을 잡고 ctime을 호출하여 로컬 변수에 저장한다.
- caller에서 넘겨준 변수에 값을 복사하고 lock을 해제한다.
- 인자로 받은 pointer 변수를 반환하면 올바른 값을 받는다.
Reentrant Functions
여러 Thread로 호출될 때 공유 변수에 Access하지 않는 경우에만 Reentrant하다.
- thread-safe 함수의 subset
- 동기화 연산이 필요하지 않다.
- Class 2 gkatnfmf thread-safe하게 만드는 방법은 reentrant
Examples
unsafe한 함수 중에 reentrant한 것도 있다.
Thread-Safe Library Functions
Standard C Library에 있는 모든 함수는 thread-safe하다.
ex) malloc, free, printf, scanf
대부분의 Unix System Call 함수는 몇 가지 제외하고 Thread-safe하다.
Race
Race는 프로그램의 정확성이 다른 Thread가 y에 도달하기 전에 x에 한 Thread가 x에 도달하는 것에 의존할 때 발생한다.
/* A threaded program with a race */
int main() {
pthread_t tid[N];
int i;
for (i = 0; i < N; i++)
Pthread_create(&tid[i], NULL, thread, &i);
for (i = 0; i < N; i++)
Pthread_join(tid[i], NULL);
exit(0);
}
/* Thread routine */
void *thread(void *vargp) {
int myid = *((int *)vargp);
printf("Hello from thread %d\n", myid);
return NULL;
}
myid = 0을 출력하려 하는데 i가 먼저 증가하여 1이 출력될 수 있다.
Deadlock
프로세스가 false 인 조건을 계속 기다리는 경우를 말한다.
- P1은 A 다음 B를 기다리고 있다.
- P2는 B 다음 A를 기다리고 있다.
- 서로 기다린다.
Deadlocking With Semaphores
Deadlock Visualized in Progress Graph
Avoiding Deadlock
같은 순서로 리소스를 획득하면 된다.
Avoided Deadlock in Progress Graph