course:nctu-高等unix程式設計:chapter12
目錄表
*Thread Control
0x00 Outline
- Introduction
- Thread limitations
- Thread attributes
- Synchronization attributes
- Thread-specific data
- Cancel options
- Threads and signals
- Threads and fork
0x01 Introduction
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
- 許多 thread function 中有著 attr 參數,一般我們使用 NULL 表示使用預設屬性
0x02 Thread limitations
- PTHREAD_DESTRUCTOR_ITERATIONS: max number of times an implementation will try to destroy the thread-specific data when a thread exits
- PTHREAD_KEYS_MAX: max number of keys that can be created by a process
- PTHREAD_STACK_MIN: min number of bytes that can be used for a thread's stack
- PTHREAD_THREADS_MAX: max number of threads that can be created in a process
#include <unistd.h> long sysconf(int name);
- 透過 sysconf function,給予名稱,我們可以得到限制數量
0x03 Thread attributes
- pthread_attr_t 資料型態
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr);
- 編譯時加上 -pthread 參數
常見的 thread attributes
- detachstate: detached thread attribute
- guardsize: guard buffer size in bytes at end of thread stack
- stackaddr: defines the base of the thread's stack
- stacksize: size of the stack (in bytes) that the system will allocate
detachstate
#include <pthread.h> int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); /* Returns zero on success, or non-zero error codes */
- detach state
- PTHREAD_CREATE_DETACHED
- PTHREAD_CREATE_JOINABLE
int makethread(void *(*fn)(void *), void *arg) { int err; pthread_t tid; pthread_attr_t attr; err = pthread_attr_init(&attr); if (err != 0) { return(err); } err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (err == 0) { err = pthread_create(&tid, &attr, fn, arg); } pthread_attr_destroy(&attr); return(err);
Thread stack address and size
- 因為 shared stack 可能不夠使用,我們會希望分配記憶體給 thread stack,可以使用 malloc 或 mmap
#include <pthread.h> int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize); /* Returns zero on success, or non-zero error codes */
- stackaddr 參數是分配的記憶體中最低可定址的位置,不一定要是 stack 的開頭
#include <pthread.h> int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr , size_t stacksize); /* Return zero on success, or non-zero error codes */
guardsize
- 為了避免單一 thread 引起的 stack overflow,在 stack 後面通常會有個 buffer,預設大小為 PAGESIZE bytes
- 可以透過把 buffer 大小設為 0 取消這個特性
- 如果 thread 發生了 stack overflow,process 會收到 error
#include <pthread.h> int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize); int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); /* Return zero on success, or non-zero error codes */
0x04 Synchronization Attributes
- 在多執行緒中我們常要探討的就是同步的問題
- 常見的同步機制包含 mutexes(互斥鎖), reader-writer locks(讀寫鎖), 和 condition variables ,他們有相似的 initialization 和 destroy functions
#include <pthread.h> int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); int pthread_condattr_init(pthread_condattr_t *attr); int pthread_condattr_destroy(pthread_condattr_t *attr); /* Returns zero on success, or non-zero error codes */
Mutex 互斥鎖
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); // 請求鎖,如果當前 mutex 已經被鎖,那這個執行緒就會卡在這,直到mutex被釋放 int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解鎖 int pthread_mutex_trylock(pthread_mutex_t *mutex); // 嘗試請求鎖,如果當前 mutex 已經被鎖或者不可用,這個函數就直接 return error,不會把執行緒卡住
常見的 Mutex Attributes
- Process-Shared
- Type
Mutex Attribute: Process-Shared
- 一般預設只有同個 process 的 thread 可以共享可以共享同個 mutex
- 透過開啟 process-shard attribute,mutex 可用在不同 process 間
#include <pthread.h> int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared); /* Returns zero on success and store pshared in pshared parameter, or non-zero error codes */ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); /* Returns zero on success, or non-zero error codes */
- pshared 參數
- PTHREAD_PROCESS_PRIVATE: mutex 只允許被同個 process(init mutex 的 process) 產生的 thread 操作,這是預設值
- PTHREAD_PROCESS_SHARED: 所有可以存取 mutex 被分配之記憶體的 thread 皆可操作 mutex
Mutex Attribute: Type
- PTHREAD_MUTEX_NORMAL: 預設值,不會做 error checking 或 deadlock detection
- PTHREAD_MUTEX_ERRORCHECK: 提供 error checking
- PTHREAD_MUTEX_RECURSIVE: 允許同個 thread 多次重複 lock 同個 mutex,然而也要執行同樣次數的 unlock 釋放這個 mutex
- PTHREAD_MUTEX_DEFAULT: 依據系統關聯自動去選擇 mutex type
Mutex type | Reloack without unlock? | Unlock when not owned? | Unlock when unlocked? |
---|---|---|---|
PTHREAD_MUTEX_NORMAL | deadlock | undefined | undefined |
PTHREAD_MUTEX_ERRORCHECK | returns error | returns error | returns error |
PTHREAD_MUTEX_RECURSIVE | allowed | returns error | returns error |
PTHREAD_MUTEX_DEFAULT | system dependent | system dependent | system dependent |
#include <pthread.h> int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type); /* Returns zero on success and store type in type parameter, or non-zero error codes */ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); /* Returns zero on success, or non-zero error codes */
Reader-Writter Lock 讀寫鎖
- mutex 的缺點是鎖住之後,其他執行緒完全無法介入,連讀取也無法,但有些執行緒只需要讀取並不會改變數值,此時互斥鎖雖然仍可使用,但必須等待解鎖,執行較花時間
- 讀寫鎖在有執行緒要去讀取時加上一個讀鎖,同時允許多個讀鎖
- 在有執行緒要去寫入時會加上一個寫鎖,寫鎖同時間只允許一個,當讀鎖為 0 時才允許寫鎖上鎖
- 多數系統的讀鎖優先權大於寫鎖,可能導致寫鎖發生飢餓狀況,不斷有讀鎖插隊上鎖導致寫鎖無法執行
- 讀寫鎖適合讀多寫少的情況,且要配置適當的優先權
#include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
0x05 Thread-Specific Data
- 又稱 thread-private data
- 我們希望每個執行緒能保有一些各自的資料,這些資料不必做同步處理
How To
- Create a pthread key: This should be done only ONCE for all threads in the same process
- Get the data associated with with key for the current thread
- If data is not available, allocate the data and associate the data with the key
- If data is no longer required, it can be released and de-associated
pthread key
- A pthread key should be created only once
#include <pthread.h> int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *)); int pthread_key_delete(pthread_key_t *key);
/* 若這樣寫,在 if 那邊可能會產生 race conditions */ void destructor(void *); pthread_key_t key; int init_done = 0; int threadfunc(void *arg) { if(!init_done) { init_done = 1; err = pthread_key_create(&key, destructor); } ... }
pthread_once_t initflag = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *initflag, void (*initfn)(void)); void destructor(void *); pthread_key_t key; pthread_once_t init_done = PTHREAD_ONCE_INIT; void thread_init(void) { err = pthread_key_create(&key, destructor); } int threadfunc(void *arg) { pthread_once(&init_done, thread_init); ... }
get, associate and de-associate data
#include <pthread.h> void *pthread_getspecific(pthread_key_t key); /* Return non-NULL for the associated value, or NULL if no value has been associated with the key */
#include <pthread.h> int pthread_setspecific(pthread_key_t key, const void *value); /* Return zero on success, or non-error error codes */
- Use a non-NULL value to associate the data
- Use a NULL data to de-associate the data, previously associated data should be retrieved and released first
static pthread_key_t key; static pthread_once_t init_done = PTHREAD_ONCE_INIT; pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER; extern char **environ; static void thread_init(void) { pthread_key_create(&key, free); } char * getenv(const char *name) { int i, len; char *envbuf; pthread_once(&init_done, thread_init); pthread_mutex_lock(&env_mutex); envbuf = (char *) pthread_getspecific(key); if (envbuf == NULL) { if((envbuf = malloc(ARG_MAX)) == NULL) { pthread_mutex_unlock(&env_mutex); return(NULL); } pthread_setspecific(key, envbuf); } len = strlen(name); for (i = 0; environ[i] != NULL; i++) { if ((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '=')) { strcpy(envbuf, &environ[i][len+1]); pthread_mutex_unlock(&env_mutex); return(envbuf); } } pthread_mutex_unlock(&env_mutex); return(NULL); }
0x06 Cancel Options
0x 參考資料
course/nctu-高等unix程式設計/chapter12.txt · 上一次變更: 由 127.0.0.1