簡體   English   中英

的std ::原子 <int> memory_order_relaxed多線程程序中的VS volatile sig_atomic_t

[英]std::atomic<int> memory_order_relaxed VS volatile sig_atomic_t in a multithreaded program

volatile sig_atomic_t是否提供任何內存順序保證? 例如,如果我只需要加載/存儲整數,是否可以使用?

例如:

volatile sig_atomic_t x = 0;
...
void f() {
  std::thread t([&] {x = 1;});
  while(x != 1) {/*waiting...*/}
  //done!
}

這是正確的代碼嗎? 有條件可能不起作用嗎?

注意:這是一個過於簡化的示例,即我不想為給定的代碼片段尋找更好的解決方案。 我只是想了解根據C ++標准,在多線程程序中我可以從volatile sig_atomic_t中得到什么樣的行為。 或者,如果是這種情況,請理解為什么行為未定義。

我在這里發現了以下聲明:

庫類型sig_atomic_t不提供線程間同步或內存排序,僅提供原子性。

如果我在這里將它與這個定義進行比較:

memory_order_relaxed:放松操作:沒有對其他讀取或寫入施加同步或排序約束,只保證此操作的原子性

是不一樣的? 究竟原子性在這里意味着什么? volatile在這里有用嗎? “不提供同步或內存排序”和“無同步或排序約束”之間有什么區別?

您正在使用sig_atomic_t類型的對象,該對象由兩個線程訪問(一個修改)。
根據C ++ 11內存模型,這是未定義的行為,簡單的解決方案是使用std::atomic<T>

std::sig_atomic_tstd::atomic<T>在不同的聯盟中。在便攜式代碼中,一個不能被另一個替換,反之亦然。

兩者共享的唯一屬性是原子性(不可分割的操作)。 這意味着對這些類型的對象的操作沒有(可觀察的)中間狀態,但就相似性而言。

sig_atomic_t沒有線程間屬性。 實際上,如果這個類型的對象被多個線程訪問(修改)(如示例代碼中所示),那么它在技術上是未定義的行為(數據競爭); 因此,未定義線程間內存排序屬性。

什么是sig_atomic_t用於?

這種類型的對象可以在信號處理程序中使用,但前提是它被聲明為volatile 原子性和volatile保證2件事:

  • 原子性:信號處理程序可以異步地將值存儲到對象中,任何讀取相同變量(在同一個線程中)的人只能觀察到之前或之后的值。
  • volatile:存儲器不能被編譯器“優化掉”,因此在信號中斷執行的點(或之后)可見(在同一線程中)。

例如:

volatile sig_atomic_t quit {0};

void sig_handler(int signo)  // called upon arrival of a signal
{
    quit = 1;  // store value
}


void do_work()
{
    while (!quit)  // load value
    {
        ...
    }
}

雖然這段代碼是單線程的, do_work可以通過觸發sig_handler並原子地改變quit值的信號異步中斷。 如果沒有volatile ,編譯器可能會“ quit ”來自quit while循環的負載,使do_work無法觀察到由信號引起的quit更改。

為什么std::atomic<T>不能用作std::sig_atomic_t的替代?

一般來說, std::atomic<T>模板是一種不同的類型,因為它被設計為由多個線程並發訪問,並提供線程間排序保證。 原子性在CPU級別並不總是可用(特別是對於較大的類型T ),因此實現可能使用內部鎖來模擬原子行為。 std::atomic<T>是否對特定類型使用鎖定T可通過成員函數is_lock_free()或類常量is_always_lock_free (C ++ 17)獲得。

在信號處理程序中使用此類型的問題是,C ++標准不保證std::atomic<T>對於任何類型T都是無鎖的。 只有std::atomic_flag才有這種保證,但這是一種不同的類型。

想象一下上面的代碼,其中quit標志是std::atomic<int> ,恰好不是無鎖的。 do_work()加載該值時,它有可能在獲取鎖之后但在釋放之前被信號中斷。 該信號觸發sig_handler() ,它現在想通過獲取do_work已經獲得的同一個鎖來存儲一個值來quit 這是未定義的行為,可能導致死鎖。
std::sig_atomic_t沒有這個問題,因為它沒有使用鎖定。 所需要的只是在CPU級別和許多平台上不可分割的類型,它可以簡單到:

typedef int sig_atomic_t;

底線是,在單線程中使用volatile std::sig_atomic_t作為信號處理程序,並在多線程環境中使用std::atomic<T>作為無數據競爭類型。

暫無
暫無

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

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