# 2021q1 Homework4 (quiz4) contributed by < `Shanola` > ## 測驗 `1` ### POSIX Thread (pthread) API 認識 透過 pthread 可以實現並行化的程式,對於多核或多處理器的系統中可以平行化或分散式執行,進而提高程式效能。相較於透過 forking 來產生的行程不必再配置記憶空間而是共享一個行程的資源。 Thread(執行緒) 的操作有 **建立 終止 同步 排程 資料管理 行程互動**: * [`pthread_create()`](https://man7.org/linux/man-pages/man3/pthread_create.3.html) 建立新的執行緒,成功時回傳 0,反之回傳錯誤值。 ```cpp= int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); void pthread_exit(void *retval); ``` 將執行緒終止的方式有幾種: 1. 在行程中呼叫 pthread_exit() 2. 在執行緒中 return 3. 在行程中呼叫 pthread_cancel() 4. 在行程中呼叫 exit() 或 return,這會終止所有 thread 透過傳入 [`pthread_exit()`](https://man7.org/linux/man-pages/man3/pthread_exit.3.html) 的參數 `retval` 可以用在 [`pthread_join()`](https://man7.org/linux/man-pages/man3/pthread_join.3.html) * [`pthread_cancel()`](https://man7.org/linux/man-pages/man3/pthread_cancel.3.html) 取消執行緒,成功時回傳 0,反之錯誤值。 ```cpp= int pthread_cancel(pthread_t thread); int pthread_setcancelstate(int state, int *oldstate); int pthread_setcanceltype(int type, int *oldtype); void pthread_testcancel(void); ``` 而一個執行緒的 cancelability state 由 [`pthread_setcancelstate()`](https://man7.org/linux/man-pages/man3/pthread_setcancelstate.3.html) 決定,具有兩種 states: - enable(預設) - PTHREAD_CANCEL_ENABLE - disable - PTHREAD_CANCEL_DISABLE 收到 cancellation request 的執行緒狀態若為 disable,會 block 直到狀態改為 enable。 當 cancellation 發生後,type 由 [`pthread_setcanceltype()`](https://man7.org/linux/man-pages/man3/pthread_setcancelstate.3.html) 決定,具有兩種 types: - deferred(預設) - PTHREAD_CANCEL_DEFFERED 意思是收到取消請求的執行緒會等到呼叫下一個 function (cancellation point) 才會被 canceled。 - asynchronous - PTHREAD_CANCEL_ASYNCHRONOUS 呼叫 [`pthread_testcancel()`](https://man7.org/linux/man-pages/man3/pthread_testcancel.3.html) 可以給執行緒一個 cancellation point,讓執行可以趕緊回應 cancellation request,如果執行緒的 state 為 disable 或沒有 request,則這個函式不會有效果。 * [`pthread_join()`](https://man7.org/linux/man-pages/man3/pthread_join.3.html) 等待執行緒終止,將其合併回行程,成功時回傳 0,反之錯誤值。 ```cpp= int pthread_join(pthread_t thread, void **retval); ``` 只有 joinable 的執行緒可以使用這個函式,若 retval 不是 NULL,則 pthread_join 會複製該目標執行緒的 exit status 到 retval,譬如執行緒被 calceled,retval 指向的值會是 `PTHREAD_CANCELED`。 若有多個執行緒同時 join,results 未定義。 * `pthread_mutex_t` Mutex (互斥鎖) 相關的函式,成功時回傳 0,反之錯誤值。 ```cpp= pthread_mutex_t mutex; int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); /* equal to pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER */ int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex); ``` 透過 [`pthread_mutex_lock() / pthread_mutex_trylock()`](https://man7.org/linux/man-pages/man3/pthread_mutex_lock.3p.html) 及 [`pthread_mutex_unlock()`](https://man7.org/linux/man-pages/man3/pthread_mutex_unlock.3p.html) 包住想要鎖住的程式,建立互斥鎖的目的是限制執行緒同時訪問相同資源,避免預期之外的結果(race conditions)。當鎖已經被一個執行緒 locked,其他想要 lock 該鎖的執行緒會被 blocked 直到鎖可以被使用。 [`pthread_mutex_destory()`](https://man7.org/linux/man-pages/man3/pthread_mutex_destroy.3p.html)可以摧毀鎖,摧毀後的鎖指向的值是 undefined,若要使用需要再次 [`pthread_mutex_init()`](https://man7.org/linux/man-pages/man3/pthread_mutex_destroy.3p.html)。 摧毀 unlock 的鎖是安全的,摧毀 locked 或有執行緒在嘗試 lock 或是鎖被其他執行緒用在 `pthread_cond_timedwait` 或 `pthread_cond_wait` 時的結果是 undefined behavior。 * `pthread_cond_t` Condition variable 相關的函式 ```cpp= pthread_cond_t cond; int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); /* equal to pthread_cond_t cond = PTHREAD_COND_INITIALIZER */ int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_timedwait(pthread_cond_t *restrict cond, thread_mutex_t *restrict mutex, const struct timespec *restrict abstime); int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); ``` 透過 [`pthread_cond_init() / pthread_cond_destroy()`](https://man7.org/linux/man-pages/man3/pthread_cond_init.3p.html) 來建立或摧毀 condition variable,後者是用來等待或稍後處理某些事情,其機制是讓執行緒暫停或放棄使用處理器直到某些條件達成,它總是**搭配 mutex 使用**以避免 deadlock 發生。 呼叫 [`pthread_cond_wait() / pthread_cond_timedwait()`](https://man7.org/linux/man-pages/man3/pthread_cond_wait.3p.html) 的執行緒會 release mutex(這將使得其他執行緒得以取得鎖來行操作),並 block 在 condition variable 上,直到呼叫 [`pthread_cond_broadcast() / pthread_cond_signal()`](https://man7.org/linux/man-pages/man3/pthread_cond_broadcast.3p.html) 後才會被喚醒。 * `pthread_condattr_t` Condition variable attributes object ```cpp= int pthread_condattr_destroy(pthread_condattr_t *attr); int pthread_condattr_init(pthread_condattr_t *attr); ``` 用來建立 condition variable attribute object,然後使用在 `pthread_cond_init()` 來建立 condition variables。 * Thread-cancellation clean-up handlers ```cpp= void pthread_cleanup_push(void (*routine)(void *), void *arg); void pthread_cleanup_pop(int execute); ``` **Clean-up handler** 可以使執行緒被 canceled 時自動執行的 function,譬如可以將已經 canceled (或其他情況) 的執行緒做 unlock,因為在執行緒如果在 locked 時被 canceled 了,會導致 pthread_join 一直 wait,除非把它 unlock。 [`pthread_cleanup_push()`](https://man7.org/linux/man-pages/man3/pthread_cleanup_push.3.html) 會將 `routine` 丟到 clean-up handler 的 stack 的 top,並以 `arg` 作為參數傳入,透過 [`pthread_clean_pop()`](https://man7.org/linux/man-pages/man3/pthread_cleanup_push.3.html) 則可移除 stack 的 top ###### tags: `linux2021`