C++多線程編程必備技能:使用thread_local實現線程隔離

一、什麼是thread_local

在多線程編程中,線程隔離是非常重要的概念。thread_local是C++11標準中新增的關鍵字,它可以保證每個線程都有自己的一份變數副本,不同線程之間互不干擾。我們可以使用thread_local來解決一些常見的線程問題,比如線程安全問題、資源競爭問題、異常處理問題等。

#include <iostream>
#include <thread>
#include <string>

thread_local int g_num = 0;  // 定義一個線程局部變數

void foo()
{
    ++g_num;
    std::cout << "Thread " << std::this_thread::get_id() << ": " << g_num << std::endl;
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);

    t1.join();
    t2.join();

    return 0;
}

在上面的代碼中,我們定義了一個線程局部變數g_num,然後在不同的線程中調用foo函數,修改g_num的值,並輸出。我們可以看到,每個線程都有自己的一份g_num副本,並且互不干擾。

二、線程安全問題與thread_local

在多線程編程中,線程安全問題是一件非常頭疼的事情。很多時候我們需要用一些鎖或者原子操作來保護共享變數,才能避免競爭問題。但是在一些情況下,我們可以使用thread_local來解決線程安全問題。比如:

1.函數內全局變數或靜態變數

#include <iostream>
#include <thread>

void foo()
{
    static thread_local int num = 0;
    ++num;
    std::cout << "Thread " << std::this_thread::get_id() << ": " << num << std::endl;
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);

    t1.join();
    t2.join();

    return 0;
}

在上面的代碼中,我們使用了函數內的static thread_local變數,這樣每個線程都有自己的一份num副本,不同線程之間互不干擾。

2.全局變數或靜態變數

#include <iostream>
#include <thread>

thread_local int g_num = 0;

void foo()
{
    ++g_num;
    std::cout << "Thread " << std::this_thread::get_id() << ": " << g_num << std::endl;
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);

    t1.join();
    t2.join();

    return 0;
}

在上面的代碼中,我們使用了全局變數thread_local int g_num來保證線程隔離。這樣每個線程都有自己的一份g_num副本,不同線程之間互不干擾。

三、使用thread_local來實現資源隔離

在一些特殊的場景下,我們需要使用線程隔離來避免資源競爭問題。比如在多線程網路編程中,每個線程都需要一個獨立的socket來進行通信。我們可以使用thread_local來保證每個線程都有自己的一份socket副本,從而避免競爭問題。

#include <iostream>
#include <thread>
#include <chrono>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

thread_local int g_socket = -1;  // 定義一個線程局部變數socket

void foo()
{
    // 每個線程都創建自己的socket
    g_socket = socket(AF_INET, SOCK_STREAM, 0);

    // 使用socket進行通信...
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 關閉socket
    close(g_socket);
}

int main()
{
    std::thread t1(foo);
    std::thread t2(foo);

    t1.join();
    t2.join();

    return 0;
}

在上面的代碼中,我們使用了thread_local int g_socket來保證線程隔離。每個線程都有自己的一份socket副本,在函數內使用該socket進行通信後再關閉。

四、使用thread_local來處理異常

在多線程編程中,異常處理也是一個非常重要的問題。比如在一個多線程的web伺服器中,如果一個請求處理出現異常,我們需要確保不影響其他請求的處理。這時候我們可以使用thread_local來保證每個線程都有自己的一份異常處理函數,從而避免影響其他線程。

#include <iostream>
#include <thread>
#include <stdexcept>

thread_local void* g_exception_handler = nullptr;

void set_exception_handler(void* handler)  // 設置該線程的異常處理函數
{
    g_exception_handler = handler;
}

void handle_exception(const std::exception& e)  // 處理異常
{
    if (g_exception_handler) {
        static_cast<void(*)(const std::exception&)>(g_exception_handler)(e);
    }
    else {
        std::cerr << "Unhandled exception: " << e.what() << std::endl;
    }
}

void foo(int n)
{
    set_exception_handler([](const std::exception& e) {
        std::cerr << "Exception in thread " << std::this_thread::get_id() << ": " << e.what() << std::endl;
    });

    if (n % 2 == 0) {
        throw std::logic_error("Even number is not allowed.");
    }
}

int main()
{
    std::thread t1(foo, 1);
    std::thread t2(foo, 2);

    t1.join();
    t2.join();

    return 0;
}

在上面的代碼中,我們使用了thread_local void* g_exception_handler來保證每個線程都有自己的一份異常處理函數。在foo函數中,我們設置該線程的異常處理函數,如果該線程拋出了異常,就會調用該異常處理函數。這樣我們就可以保證不同線程之間的異常處理互不干擾。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/312483.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2025-01-06 09:42
下一篇 2025-01-06 09:47

相關推薦

發表回復

登錄後才能評論