Reentrant vs Thread-safe
Part 3: C 語言例子 (thread-safe function)
接下來, 我們來看一些 thread-safe 的例子: 首先是在 wiki 網站上的一個 Thread-safe 但不是 reentrant 的例子.
#include <pthread.h>
int increment_counter() {
static int counter = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
// only allow one thread to increment at a time
++counter;
// store value before any other threads increment it further
int result = counter;
pthread_mutex_unlock(&mutex);
return result;
}
這個例子中的 increment_counter() 可以被多個執行緒呼叫而不會產生任何問題, 因為它用了一個 mutex 來保護 (同步) 所有對共用的靜態變數 counter 的存取. 但是如果中斷服務程式 ISR 也呼叫了 increment_counter(), 就會很容易使系統當掉. 原因是如果中斷發生在執行緒正呼叫 increment_counter() 時 (尤其是 mutex lock 和 unlock 之間), 那 ISR 將永遠等不到 mutex 被 unlock. 因為 CPU 接受中斷進入 ISR 後, 只有 ISR 完成, 才會回到執行緒. 記住: 中斷永遠比正常執行優先, 所以 ISR 要比執行緒或者是 OS 核心優先執行.
MagicJackTing 發表在 痞客邦 留言(1) 人氣()
Reentrant vs Thread-safe
Part 2: C 語言例子 (reentrant function)
先來看 reentrant 的例子: 最常見到用來說明 reentrant 的例子大概就屬 swap() 了
int t;
void swap(int *x, int *y) {
t = *x;
*x = *y;
*y = t;
}
void isr(void) {
int x = 1, y = 2;
swap(&x, &y);
...
return;
}
void main(void) {
int x = 3, y = 4;
...
swap(&x, &y);
...
}
這個例子中的 swap() 函數是 non-reentrant function (而且也不是 thread-save). 原因是第4行及第6行用到共用的變數 t. 即便是我們用的是 32bit CPU, 甚至也有技援 mem. to mem. 移轉資料不被中斷, 也還是有二個時間點(在第5行執行前或執行後) 發生中斷會產生錯誤的執行結果. 如下表的 NG Example 1 和 NG Example 2 所示 (在這裡為了讓這一張表格不致於太長, 我只把動作拆解到高階語言的單一指令階段, 實際上像 *x=*y 這樣的指令可能就要二個以上的低階組合語言指令.)
MagicJackTing 發表在 痞客邦 留言(0) 人氣()
Reentrant vs Thread-safe
Reentrancy 和 thread-safty 是兩個容易被搞混了的觀念. 其中最嚴重的是誤以為 reentrant function 必定是 thread-safe 或者相反以為 thread-safe function 必為 reentrant, stackoverflow 網站上的答覆甚至同時出現二種答案的現象.
Reentrancy 和 Thread-safty 二者的差異
首先來看 reentrancy: 字面上的意思是可重入. Reentrancy 原先是討論單一執行緒環境下 (即沒有使用多工作業系統時) 的主程式和中斷服務程式 (ISR) 之間共用函數的問題. 當然現在多核心的 CPU 盛行, 討論範圍也必需擴充至多執行緒的情況. 重點是它討論的主體是: 在 ISR 中使用的函數 (不論是自己寫的或者是函數庫提供的) 是否會引發錯誤結果. 主要的達成條件是二者 (ISR 和非 ISR) 的共用函數中不使用靜態變數或全域變數 (意即只用區域變數). 一般是撰寫驅動程式 (device driver) 或者是寫 embedded system 的人會遇到這個問題.
再來是 thread-safety: 字面上的意思是執行緒 (線程) 安全. Thread-safe 一開始就針對多執行緒的環境 (CPU 可能單核也可能是多核), 討論的是某一段程式碼在多執行緒環境中如何保持資料的一致性 (及完整性), 使不致於因為執行緒的切換而產生不一致 (及不完整) 或錯誤的結果. 所以是程式中有運用到多執行緒的大型應用系統的程式人員會比較常遇到這類問題. 問題的產生點一般出現在對某一共用變數 (或資源) 進行 read-modify-write (或者類似的動作(註一)) 時, 還沒來得及完成整個動作, 就被其他的執行緒插斷, 並且該執行緒也一樣對這個共用變數 (或資源) 進行 read-modify-write (或者類似的動作). 例如Thread1 和 Thread2 之間我們需要一個作為計數器的共用變數:
MagicJackTing 發表在 痞客邦 留言(4) 人氣()