簡體   English   中英

C ++ 11 thread_local變量可以從父線程繼承其初始值嗎?

[英]Can a C++11 thread_local variable inherit its initial value from the parent thread?

我想要一個thread_local變量來更改應用程序每個線程中應用的日志記錄級別。 像這樣:

enum class trace_level { none, error, warning, log, debug, verbose };
static thread_local trace_level min_level = trace_level::log;

應用程序啟動時,主線程的默認值應為trace_level::log ,但如果在啟動其他線程之前對其進行了更改,則我希望子線程以父級的當前值開始。

有什么辦法可以使用thread_local變量嗎? 由於此代碼埋在庫中,因此不能簡單地在每個線程的開頭手動設置該值。

您可以創建指向父線程局部變量的全局指針。

在全球范圍內

thread_local trace_level min_level = trace_level::log;
trace_level *min_level_ptr = nullptr;

然后,您可以在每個線程中執行以下操作:

if (!min_level_ptr)
    min_level_ptr = &min_level;
else
    min_level = *min_level_ptr;

(可能的是,使min_level_ptr原子為增加安全性,並使用原子比較交換而不是賦值)。

這個想法是這樣的:每個線程的本地存儲在內存中占據不同的區域,因此一個線程中的min_level變量具有與所有其他線程不同的唯一存儲地址。 另一方面, min_level_ptr具有相同的地址,無論哪個線程正在訪問它。 當“父”線程在所有其他線程之前啟動時,它將聲明具有其自己的min_level地址的全局共享指針。 然后,子代將從該位置初始化其值。

如果初始化是動態的,這已經發生。 該標准要求具有“線程存儲持續時間”和動態初始化的變量在線程開始和“首次使用”之間的某個時間進行初始化。 但是,由於您通常無法確切控制初始化的發生時間(除了創建線程對象之后的某個時間和線程結束之前的某個時間(假定線程局部變量實際上已被線程使用),所以問題在於線程局部變量可能會使用創建主線程設置的值進行初始化。

作為一個具體的例子,請考慮:

#include <stdio.h>

#include <chrono>
#include <functional>
#include <thread>
#include <string>

using std::string;

enum class trace_level { none, error, warning, log, debug, verbose };

trace_level log_level = trace_level::log;


static thread_local trace_level min_level = log_level;
void f(string const& s)
{

    printf("%s, min_level == %d\n", s.c_str(), (int) min_level);
}



int main()
{
    std::thread t1{std::bind(f,"thread 1")};

    //TODO: std::this_thread::sleep_for(std::chrono::milliseconds(50));

    log_level = trace_level::verbose;
    std::thread t2{std::bind(f,"thread 2")};

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

上面注釋了sleep_for()調用后,通常會得到以下輸出:

C:\so-test>test
thread 1, min_level  == 5
thread 2, min_level  == 5

但是,在sleep_for()注釋sleep_for() ,我再次得到(通常):

C:\so-test>test
thread 1, min_level  == 3
thread 2, min_level  == 5

因此,只要您願意在線程啟動后不久在主線程中更改該級別,就可以確定該線程將獲得哪個日志記錄級別,就可以忍受一點不確定性,您就可以按照自己的意願去做很自然

還有一個警告:數據競賽。 上面的代碼在log_level變量上具有數據競爭,因此它實際上具有未定義的行為。 解決的辦法是使變量成為原子類型,或將其包裝在使用互斥體來保護更新和從數據競爭中讀取的類中。 因此,將全局log_level的聲明更改為:

std::atomic<trace_level> log_level(trace_level::log);

標准引用:

3.6.2非局部變量的初始化[basic.start.init]

...具有線程存儲持續時間的非局部變量由於線程執行而被初始化。 ...

3.7.2 / 2線程存儲持續時間[basic.stc.thread]

具有線程存儲持續時間的變量應在其第一次使用odr(3.2)之前初始化,如果構造,則應在線程退出時銷毀。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM