Thread thread๋ process์ ๊ฒฝ๋ํ ๋ฒ์ ์ผ๋ก ์๊ฐํ ์ ์๋ค. pthread_create() ํจ์๋ก fork ๋ช
๋ น์ ๋์ฒดํ๊ณ , pthread_join() ์ผ๋ก wait ๋ช
๋ น์ ๋์ฒดํ๋ฉด process ๋์ thread๋ฅผ ๋์์ํจ๋ค. thread๋ ํจ์๋ฅผ ์คํ์ํค๋ ๊ฒ์ด ๊ธฐ๋ณธ์ด๋ฉฐ, ํจ์๋ฅผ ์คํ์ํฌ ๋ ๋ฃ์ ์ธ์์, ํจ์์ ๋ฆฌํด๊ฐ์ ๋ฐ์ ์ธ์๋ฅผ pthread_create์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋๋ค. ๋ฆฌ๋
์ค ํ๋ก์ธ์ค ํ์ ๋ชฉ๋ก์ LWP(light-weight-process) ํญ๋ชฉ์ผ๋ก ํ์๋๋ฉฐ, proces ID๊ฐ ๊ฐ๋๋ผ๋ LWP ID๊ฐ ๋ค๋ฅด๋ฉด ๊ฐ์ process ์์ thread์ธ ๊ฒ. pthread_exit() ๋ก thread๋ง ์ข
๋ฃ์ํฌ ์ ์๋ค. main process๊ฐ ์ข
๋ฃ๋๋ฉด ๋ธ๋ ค์๋ thread๋ค๋ ํจ๊ป ์ข
๋ฃ๋๋ค. ๋ค๋ง, main thread๋ง pthread_exit์ผ๋ก ์ข
๋ฃ์ํค๋ฉด process๊ฐ ์ข
๋ฃ๋์ง ์๊ณ main thread๋ง ์ข
๋ฃ๋๊ณ ๋ค๋ฅธ thread๋ค์ ๊ณ์ ๊ตฌ๋๋๋ ํํ๊ฐ ๋๋ฏ๋ก ์ฃผ์ํ๋ค. int pthread_join(pthread_t thread, void **retval) : ์์ thread๊ฐ ์ข
๋ฃ๋ ๋ ๊น์ง ๋๊ธฐํ๊ณ , ์ข
๋ฃ์ฒ๋ฆฌ๋ฅผ ํด ์ฃผ๋ ํจ์, pthread_exit()์์ ๋ฐํ๋ ๊ฐ์ retval๋ก ๋ฐ์์ฌ์ ์๋ค. pthread_detach(int tid) : thread id๊ฐ tid์ ํด๋นํ๋ thread๋ฅผ ๋ถ๋ชจ thread์์ ๋ถ๋ฆฌํ๋ ํจ์. ์ดํ ์ข
๋ฃ๋๊ณ join ์ฒ๋ฆฌ๋ฅผ ๋๊ธฐํ์ง ์๊ณ ๋ฐ๋ก free๋จ. int pthread_self() : ์์ ์ thread id ๋ฅผ ํ์ธํ ๋ ์ฌ์ฉํ๋ ํจ์ void* func(void* data)
{
(struct ARG*) data;
...
pthread_detach(pthread_self()); // pthread_join ๋์ ์ฌ์ฉ ๊ฐ๋ฅ
}
...
// thread ์์ฑ
struct ARG *arg;
int tid = pthread_create(&thread, 0, func, arg);
...
pthread_join(tid, 0); // pthread_detach ๋์ ์ฌ์ฉ ๊ฐ๋ฅ
... process ์ thread ์ฐจ์ด process๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์์ ํ๋ ์๊ฐ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ๋ฆฌ๋์ง๋ง, thread๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ณต์ ํ์ฌ ์์ ํ๊ณ ๋์๋ ๊ฐ์ ์์ญ์ ์ฐธ์กฐํ ์ ์๋ค. (์ ์ฒด ๊ฐ์๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ณต์ ํ๋ค.) process๋ wait ๊ฐ์ ์ธ์๋ฅผ ํ์ธ ์๋ฌ๋ฅผ ํ์ธํ ์ ์๋ ๋ฐ๋ฉด, thread์ ์๋ฌ๋ pthread_join์ return ๊ฐ์ ํ์ธํ๋ค. (๊ฐ์ด 0 ์ด๊ณผ์ด๋ฉด ์๋ฌ๊ฐ ๋จ) ์ผ๋ฐ์ ์ธ ์๋ฌ ์ฒ๋ฆฌ๋, errno.h ํค๋ํ์ผ์ errno ๋ผ๋ ๋ณ์๊ฐ ์ ์ญ๋ณ์๋ก ์ ์ธ๋์ด ์๊ณ , ํ๋ก์ธ์ค๊ฐ ์๋ฌ์ ์ํด ์ข
๋ฃ๋ ๊ฒฝ์ฐ ์ด ๋ณ์์ ๊ฐ์ ์ฑ์๋ฃ๋๋ค. thread๋ ์ ์ญ๋ณ์๋ฅผ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ errno๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒ Mutex ์ ์ญ๋ณ์์ ์ํธ ์ฐธ์กฐ์ ์ํด ๋ฐ์ํ๋ race condition ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ race condition : ๋ ์ด์์ thread๊ฐ ์ ์ญ๋ณ์๋ฅผ ์ฐธ์กฐํ ๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผํ๋ ค ์๋ก ๊ฒฝ์ํ๋ ์ํฉ pthread.h ํค๋๋ฅผ ์ฌ์ฉํ๋ฉฐ, pthread ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋น๋์ ์ต์
์ -lpthread๋ฅผ ์ถ๊ฐํด์ค๋ค. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
...
pthread_mutex_lock(&mutex);
// ์ ์ญ๋ณ์ ์ฐธ์กฐ ์์ญ
pthread_mutex_unlock(&mutex); mutex๋ฅผ ์ฌ์ฉํด ์๊ณ ์์ญ(critical section)์ ๋ํด mutual exclusion ์์ฑ์ ๋ณด์ฅํ์ฌ ๋์ ์ ์์ ์ํ ์ค๋์์ ๋ง์ ์ ์๋ค. Mutex ๋ด๋ถ ๊ตฌ์กฐ C์ธ์ด๋ก๋ compare์ set์ atomicํ๊ฒ ์ํํ ์ ์์ด mutual exclusion์ ๊ตฌํํ ์ ์๋ค. cas(compare and set) ๋ผ๋ ์ฝ๋๋ฅผ ์ด์
๋ธ๋ฆฌ์ด์์ ์ง์ํ๋๋ฐ, compare์ set์ atomicํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค. ์๋ ํจ์๋ ์ด์
๋ธ๋ฆฌ์ด๋ฅผ ์ฌ์ฉํ์ฌ C์์ cas๋ฅผ ๊ตฌํํ ๋ด์ฉ์ด๋ค. cpu ์นฉ๋ง๋ค ์ง์ํ๋ ํํ๊ฐ ๋ค๋ฅผ ์ ์์์ ์ฃผ์ํ๋ค. (์๋๋ ์ธํ
์ด ์ ๊ณตํ๋ ํํ) typedef int int32_t;
int mutex = 0; // ์ด๊ธฐ๊ฐ 0
/**
* @brief old_value์ *ptr์ ๋น๊ตํ์ฌ ๊ฐ๋ค๋ฉด *ptr์ new_value๋ฅผ ๋์
ํ๋ค.
* mutex lock์ ์ญํ ์ ํ๋ค. * @return int old_value์ *ptr์ ๋น๊ต ๊ฒฐ๊ณผ๊ฐ ๊ฐ๋ค๋ฉด false๋ฅผ, ๋ค๋ฅด๋ค๋ฉด true๋ฅผ ๋ฐํ
*/
int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr)
{
int 32_t prev;
__asm__ __volatile__ ("lock; cmpxchgl %1, %2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev != old_value;
} Spin Lock while๋ฌธ์ ๋ฐ๋ณตํ๋ฉฐ mutex๋ฅผ ๊ณ์ ์ฒดํฌํ๋ ๊ธฐ๋ฒ void spin_lock(int* mutex)
{
while (__bionic_cmpxchg(0, 1, mutex)); // mutex๊ฐ 0์ด ๋ ๋ ๊น์ง ๋ฌดํ ๋๊ธฐ } CPU ํ์ฉ๋๊ฐ ๋จ์ด์ง๋ฏ๋ก ์๊ณ์์ญ์ด ์งง์ ๊ฒฝ์ฐ๋ง ์ฌ์ฉ ๊ถ์ฅ Sleep Lock mutex๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๋์ thread๋ฅผ sleep ์ํค๋ฉด thread์ ํ ๋น๋ ๋ฆฌ์์ค๋ฅผ ํด์ ํ์ฌ ๋ค๋ฅธ ๊ณณ์ ํ ๋นํด ์ค ์ ์๊ฒ ๋๋ค. gcc์์๋ slelep lock์ ์ง์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ง๋ง, ์์คํ
์ปค๋งจ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ futex(fast user mutex)๋ผ๋ ํจ์๋ก sleep lock์ ์ง์ํ๋ค. C์ธ์ด๋ก ์ฌ์ฉํ๋ ค๋ฉด ์์คํ
์ฝ๋ก futex๋ฅผ ํธ์ถํ๋ฉด ๋๋ค. #include <unisted.h>
int mutex = 1;
void *foo(void *data)
{
sytstemcall 202, &mutex, 0, 1, 0); // __futex_wait();
... /* critical section */
systemcall(202, &mutex, 1, 1); // __futex_wake();
} __futex_wait ์ mutex_lock, __futex_wake๋ mutex_unlock์ ๋์๋๋ค. Self Lock recursive ํจ์์์ mutex๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํ๋์ ํจ์์์ ๋์ผํ mutex๋ฅผ ๋๋ฒ ํธ์ถํ๊ฒ ๋๋ โselfl lock"์ด ๋ฐ์ํ ์ ์๋ค. self lock์ด ๋ฐ์ํ๋ฉด ๋ง์ฐฌ๊ฐ์ง๋ก deadlock์ด ๋ฐ์ํ๋ค. ์ฌ๊ท ํธ์ถ์ ์ํ recursive mutex lock ์ด ์กด์ฌํ๋ค. mutex ์์ฑ์ attribute๋ก ์ฌ๊ทํจ์๋ฅผ ์ํ ์ค์ ์ด ์กด์ฌํ๋ฉฐ, mutex_lock์ ํ thread์์ ์ค๋ณต ํธ์ถ ๊ฐ๋ฅํ๋ฉฐ, mutex_lock์ ํธ์ถํ ์๋งํผ mutex_unlock์ ํธ์ถํด ์ฃผ๋ฉด mutex๊ฐ ํด์ ๋๋ค. pthread_mutexattr_t attr;
pthread_mutex_t mutex;
...
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutuex_init(&mutex, &attr);
...
pthread_mutex_lock(&mutex); // 1
pthread_mutex_lock(&mutex); // 2
pthread_mutex_lock(&mutex); // 3
...
pthread_mutex_unlock(&mutex); // 1
pthread_mutex_unlock(&mutex); // 2
pthread_mutex_unlock(&mutex); // 3 Condition (์กฐ๊ฑด๋ณ์) thread์์ ์ ์ญ ๋ณ์์ ์ฐธ์กฐํ ๋, ์์๋ฅผ ์ ์ดํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ ์๋ ์ฝ๋๋ thread1์์ ์ ์ญ๋ณ์๋ฅผ ์ฒ๋ฆฌํด์ผ thread2์์ ์ ์ญ๋ณ์์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๊ฒ ํ๋ ์ฝ๋์ด๋ค. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
...
thread1()
{
pthread_mutex_lock(&mutex);
// ์ ์ญ๋ณ์ ์ฒ๋ฆฌ
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
...
thread2()
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond);
// ์ ์ญ๋ณ์ ์ฒ๋ฆฌ
pthread_mutex_unlock(&mutex);
} Condition ๋ด๋ถ ๊ตฌ์กฐ func1()
{
pthread_mutex_lock(); // <- (1)
do_something();
pthread_cond_signal();
pthread_mutex_unlock();
}
func2()
{
pthread_mutex_lock();
pthread_cond_wait(); // <- (2)
do_something();
pthread_mutex_unlock();
} func1์ด (1)์์ mutex unlock์ ๋๊ธฐํ๊ณ , func2๊ฐ (2)์์ condition signal์ ๋๊ธฐํ๋ฉด deadlock์ด ๊ฑธ๋ฆด ๊ฒ ๊ฐ์ง๋ง, condition wait์ mutex lock์ ์๋ก ๊ต์ฐฉ์ํ๋ฅผ ๋ง๋ค์ง ์๋๋ค. pthread_cond_wait() ์๋์ ๊ฐ์ด mutex_unlock, futex_wait, mutex_lock์ผ๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฏ๋ก mutex pthread_cond_wait()
{
...
pthread_mutex_unlock();
while (condition == 0) // condition ์กฐ๊ฑด์ด ์ถฉ์กฑ๋ ๋ ๊น์ง ๋ฌดํ ๋๊ธฐ
{
futex_wait(); // sleep lock
}
condition = 0; // condition ์ด๊ธฐํ
pthread_mutex_lock();
...
} ์ฆ, condition wait๋ condition signal์ด ๋ฐ์ํ ์์ ์ด ์๋๋ผ, mutex ๊ฐ unlock๋๋ ์์ ์ ํ์ถ๋๋ค. Deadlock ๋ ๊ฐ ์ด์์ Mutex๊ฐ ์๋ก ํด์ ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉฐ ๋๊ธฐํ์ฌ ๋ ์ด์ process๊ฐ ์งํ๋์ง ๋ชปํ๊ฒ ๋๋ ์ํฉ์ deadlock์ด๋ผ ํ๋ค. lock์ ์์๋๋ก ์ก๊ณ , cycle์ด ์๊ธฐ์ง ์๊ฒ ๊ด๋ฆฌํ๋ฉด deadlock์ ํผํ ์ ์๋ค. ์ฌ์ง์
๊ฐ๋ฅ ํจ์ (Reentrant) thread์์ ์ฌ์ฉํ ์ ์๋ ํจ์๋ฅผ โ์ฌ์ง์
๊ฐ๋ฅ ํจ์โ ๋ผ ํ๋ค. ์ฆ, Thread-safe ํ ํจ์๋ฅผ ์๋ฏธํ๋ค. ๋ด๋ถ์ ์ผ๋กฑ ์ ์ญ๋ณ์, ํน์ static ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ํจ์๋ โ์ฌ์ง์
๋ถ๊ฐ๋ฅโ ํ๋ค. strtok๋ ๋ํ์ ์ธ ์ฌ์ง์
๋ถ๊ฐ๋ฅํ ํจ์์ด๋ค. func1()
{
strtok()
}
func2()
{
strtok()
}
main()
{
pthread_create(func1);
pthread_create(func2);
}
// -> strtok๋ ์ฌ์ง์
๋ถ๊ฐ๋ฅํ ํจ์์ด๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ณผ๊ฐ ์๋ํ ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง ์์ ์ ์๋ค. C ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ strtok_r ์ด๋ผ๋ ์ฌ์ง์
๊ฐ๋ฅํ ํจ์๋ฅผ ์ ๊ณตํ๋ค. TLS / TSD TLS๋ thread ์ ์ ์ญ ๋ณ์๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ๊ณต๊ฐ์ผ๋ก, ๋ก๋(Loader)์ ์ํด์ ํ ๋น๋๋ค. ๋ฆฌ๋
์ค์์๋ TSD๋ผ ๋ถ๋ฅธ๋ค. int pthread_setspecific(pthread_key_t key, const void *value) : โkeyโ ์ ํด๋นํ๋ ์์ญ์ โvalueโ๋ฅผ ์ฐ๊ฒฐํ๋ค. value๋ก๋ ๋์ ํ ๋นํ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์จ๋ค. void* pthread_getspecific(pthread_key_t key) : ๊ธฐ์กด์ set์ผ๋ก ํ ๋นํ key์ ํด๋นํ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ ธ์จ๋ค. void pthread_key_create(pthread_key_t key, void* (*descturctor)(void*)) ํ ๋นํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๋ ์ญํ ์ ์ํํ ํจ์ void destructor(void* ptr){free(p);}๋ฅผ ์ ์ํ๊ณ , destructor์ ํฌ์ธํฐ๋ฅผ key์ ๋งคํ์ํจ๋ค. void main(void)
{
pthread_key_t key;
pthread_key_create(key, void (*destructor)(void*));
}
void func1(void)
{
int *tsd = pthread_get_specific(key) // key์ ํด๋นํ๋ ์์ญ ๊ฐ์ ธ์ด
if (!tsd) // null ๋ฐ์์ ์
{
tsd = calloc(1, sizeof int); // int ์์ญ์ด ํ์ํด์ ๋์ ํ ๋น. ๋ค๋ฅธ ์๋ฃํ๋ ๊ฐ๋ฅ
pthread_set_specific(key, tsd); // TSD ์์ญ์ ์ ์ฅ
}
}
void destructor(void* ptr)
{
free(p);
} TLS๋ ๋ด๋ถ์ ์ผ๋ก void* tls[] ๋ฐฐ์ด์ bitmap ํํ๋ก ์ง๋๊ณ , pthread_set_specific์ ํ ๊ฒฝ์ฐ tls[idx]์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ๋์
ํ๋ค. pthread_set_specific์ ํธ์ถํ ๋ ๋ง๋ค idx๋ ์๋์ผ๋ก ๊ฐฑ์ ๋๋ค. thread๊ฐ ์ข
๋ฃ๋ ๋ ๋ชจ๋ key์ ๋ํด ์๋ฉธ์๋ก ์ ์๋ destructor๊ฐ ํธ์ถ๋๋ค.