首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言多執行緒併發陷阱:全域變數非原子累加導致資料競爭

     編輯:關於C

問題現場

100 執行緒各對全域 g_iTestInteger 執行 ++,主執行緒列印結果。預期 100,實際輸出 98~99 不定,且多次執行結果不同。

根因分析:++ 非原子操作

g_iTestInteger++ 編譯為三條指令:

  1. LOAD 從記憶體讀取值到暫存器。
  2. INC 暫存器加 1。
  3. STORE 寫回記憶體。

兩執行緒同時 LOAD 同一值 (如 5),各自 INC 為 6,先後 STORE 6,導致少算 1。此為資料競爭。

驗證:加入列印觀察執行順序

執行緒內列印計數值,發現序號亂序、主執行緒列印時仍有子執行緒在跑 (輸出 98 後仍出現 99, 100, 74...)。

解決方案對比

方案原理優點缺點
主執行緒 sleep 等待時間換空間簡單不可靠、浪費 CPU、無法確定等多久
pthread_join 回收執行緒阻塞等待特定執行緒結束標準、精確、無競爭需儲存所有 thread_id
互斥鎖 pthread_mutex_lock序列化關鍵區通用、精確控制效能開銷、死鎖風險
原子操作 __sync_fetch_and_add / C11 atomic_fetch_add硬體級原子指令無鎖、高效能需編譯器/硬體支援、僅限簡單操作

標準修正:pthread_join 版

#include 
#define THREAD_NUM 100
uint32_t g_iTestInteger = 0;

void *ProcessTask(void *arg) {
    // 方案 A: 互斥鎖 (需全域 mutex 初始化)
    // pthread_mutex_lock(&mutex);
    // g_iTestInteger++;
    // pthread_mutex_unlock(&mutex);
    
    // 方案 B: 原子操作 (GCC/Clang 內建)
    __sync_fetch_and_add(&g_iTestInteger, 1);
    return NULL;
}

int main() {
    pthread_t tids[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; i++)
        pthread_create(&tids[i], NULL, ProcessTask, NULL);
    
    for (int i = 0; i < THREAD_NUM; i++)
        pthread_join(tids[i], NULL); // 等待所有結束
    
    printf("In main, TestInteger = %u\n", g_iTestInteger); // 穩定輸出 100
    return 0;
}

核心結論

  1. 多執行緒共享寫入變數必須同步 (鎖/原子/無鎖資料結構)。
  2. pthread_join 是等待執行緒結束的正規手段,sleep 是反模式。
  3. 若操作高度耦合 (如單一計數器),單執行緒循環往往比多執行緒+鎖更快、更簡單。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved