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

C/C++ 線程本地存儲 (TLS) 實戰:多線程文件複製緩存隔離

     編輯:關於C++

應用場景

多線程並發調用文件複製接口,每線程需獨立緩存區。若用全局 static 緩存,多線程數據污染;若每次分配釋放,內存碎片嚴重。TLS 完美解決:線程創建時分配緩存,線程結束自動釋放,僅對當前線程可見。

核心 API (pthread)

  • pthread_key_create(&key, destructor):創建 TLS 鍵,可指定析構函數
  • pthread_setspecific(key, value):綁定線程私有數據
  • pthread_getspecific(key):獲取當前線程私有數據
  • pthread_key_delete(key):銷毀鍵(不調用析構函數)

完整示例代碼


#include <iostream>
#include <pthread.h>
#include <windows.h>
#include <stdio.h>

typedef enum {
    kTLSKeyCopyFileBuf = 0,
    kTLSKeyOthers,
    kTLSKeyMax
} kTLSKey;

static pthread_key_t gTLSKey[kTLSKeyMax];
static int gTLSKeySize[kTLSKeyMax] = {4194304, 4096}; // 4MB, 4KB
static pthread_barrier_t barrier;
static int THREADCOUNT = 10;

// 庫接口 1:初始化 TLS 鍵
void StartWork(kTLSKey type) {
    pthread_key_create(&gTLSKey[type], NULL);
}

// 庫接口 2:銷毀 TLS 鍵
void EndWork(kTLSKey type) {
    pthread_key_delete(gTLSKey[type]);
}

// 庫接口 3:文件複製(演示 TLS 緩存使用)
void MyCopyFile(const char* from, const char* to, void* data) {
    pthread_key_t& key = gTLSKey[kTLSKeyCopyFileBuf];
    char* lpvData = (char*)pthread_getspecific(key);
    assert(lpvData == data); // 驗證 TLS 隔離性
    Sleep(100); // 模擬複製耗時
}

void* ThreadFunc(void* data) {
    int size = gTLSKeySize[kTLSKeyCopyFileBuf];
    pthread_key_t& key = gTLSKey[kTLSKeyCopyFileBuf];
    char* buf = (char*)malloc(size);
    std::cout << GetCurrentThreadId() << " Create buf size: " << size << ": " << (int*)buf << std::endl;
    pthread_setspecific(key, buf); // 綁定到當前線程

    // 每線程複製 100 個文件
    for (int i = 0; i < 100; ++i) {
        MyCopyFile("C:\\infoworld.txt", "E:\\infoworld.txt", buf);
    }
    free(buf);
    std::cout << "ThreadFunc: " << GetCurrentThreadId() << " finish" << std::endl;
    pthread_barrier_wait(&barrier);
    return NULL;
}

void TestPthreadLocalStorage() {
    StartWork(kTLSKeyCopyFileBuf);
    pthread_barrier_init(&barrier, NULL, THREADCOUNT + 1); // +1 主線程

    for (int i = 0; i < THREADCOUNT; ++i) {
        pthread_t t;
        pthread_create(&t, NULL, ThreadFunc, NULL);
        pthread_detach(t);
    }
    pthread_barrier_wait(&barrier);
    EndWork(kTLSKeyCopyFileBuf);
}

int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    std::cout << "test thread local storage." << std::endl;
    TestPthreadLocalStorage();
    std::cout << "end thread local storage." << std::endl;
    return 0;
}

運行輸出分析

test thread local storage.
5168 Create buf size: 4194304: 0x1180020
3892 Create buf size: 4194304: 0x1790020
3352 Create buf size: 4194304: 0x21a0020
...(10 個線程各自分配獨立 4MB 緩存,地址互不相同)
ThreadFunc: 5168 finish
ThreadFunc: 3892 finish
...
end thread local storage.

每線程緩存地址唯一,驗證了 TLS 隔離性。線程結束自動釋放(此處手動 free,實際可用析構函數自動管理)。

Windows 原生 TLS 對比

  • TlsAlloc/TlsFree/TlsSetValue/TlsGetValue:Windows API,功能類似
  • __declspec(thread):編譯期 TLS,僅支持 POD 類型,不支持動態初始化
  • pthread 版本跨平台(Linux/Windows/pthreads-win32),推薦通用代碼使用

最佳實踐

  1. 庫初始化時創建 TLS 鍵,卸載時刪除
  2. 線程首次使用時 malloc 綁定,線程退出前 free(或註冊析構函數)
  3. 避免在 TLS 析構函數中調用複雜同步操作
  4. 緩存大小可配置,避免過大導致虛擬內存耗盡
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved