簡體   English   中英

std :: mutex :: try_lock虛假失敗?

[英]std::mutex::try_lock spuriously fail?

也許std::mutex::try_lock 有誤解

即使互斥鎖當前未被任何其他線程鎖定,也允許此函數虛假失敗並返回false。

這意味着如果沒有一個線程鎖定該mutex ,當我嘗試try_lock返回false 出於什么目的?

是不是功能try_lock返回false ,如果它的鎖定 true ,如果沒有人鎖了嗎? 不確定我的非母語英語是否在欺騙我...

這意味着如果沒有一個線程鎖定該互斥鎖,當我嘗試try_lock時,它可能返回false?

是的,這正是它所說的。

如果沒有鎖定,try_lock的函數是否會返回false,如果它被鎖定則返回true?

不, try_lock的功能是嘗試鎖定互斥鎖。

但是,失敗的方法不止一種:

  1. 互斥鎖已經鎖定在別處(這是你想到的那個)
  2. 某些特定於平台的功能會中斷或阻止鎖定嘗試,並且控制權將返回給可以決定是否重試的調用方。

在POSIX-ish平台上,從POSIX線程繼承的常見情況是,信號被傳遞到當前線程(並由信號處理程序處理),從而中斷鎖定嘗試。

在其他平台上可能存在其他特定於平台的原因,但行為是相同的。

根據你的意見,我會寫(引用你的話):

std::unique_lock<std::mutex> lock(m, std::defer_lock); // m being a mutex
...
if (lock.try_lock()) {
  ... // "DO something if nobody has a lock"
} else {
  ... // "GO AHEAD"
}

請注意, lock.try_lock()有效地調用m.try_lock() ,因此它也容易出現虛假失敗。 但我對這個問題並不在意。 IMO,在實踐中,虛假失敗/喚醒是非常罕見的(正如無用的指出,在Linux上,它們可能在信號傳遞時發生)。

有關虛假問題的更多信息,請參閱: https//en.wikipedia.org/wiki/Spurious_wakeup為什么pthread_cond_wait有虛假的喚醒?

UPDATE

如果你真的想要消除try_lock虛假失敗,你可以使用一些原子標志,例如:

// shared by threads:
std::mutex m;  
std::atomic<bool> flag{false};

// within threads:
std::unique_lock<std::mutex> lock(m, std::defer_lock); // m being a mutex
...
while (true) {
  lock.try_lock();
  if (lock.owns_lock()) {
    flag = true;
    ... // "DO something if nobody has a lock"    
    flag = false;
    break;
  } else if (flag == true) {
    ... // "GO AHEAD"
    break;
  }
}

它可能可能被重寫為更好的形式,我沒有檢查。 此外,請注意, flag不會通過RAII自動取消設置,某些范圍保護可能在此處有用。

更新2

如果您還不需要mutex的阻止功能,請使用std::atomic_flag

std::atomic_flag lock = ATOMIC_FLAG_INIT;

// within threads:
if (lock.test_and_set()) {
    ... // "DO something if nobody has a lock"    
    lock.clear();
} else {
    ... // "GO AHEAD"
}

再次,通過一些RAII機制清除標志會更好。

如果對try_lock()的調用返回true,則調用成功鎖定鎖。 如果沒有,則返回false。 就這樣。 是的,當沒有其他人擁有鎖時,該函數可以返回false。 False 表示鎖定嘗試沒有成功; 它沒有告訴你它失敗的原因。

與那里說的不同,我認為沒有任何理由因為操作系統相關的原因導致try_lock函數失敗:這樣的操作是非阻塞的,因此信號不能真正中斷它。 很可能它與在CPU級別上如何實現此功能有關。 畢竟,無爭議的案例通常是互斥體最有趣的案例。

互斥鎖定通常需要某種形式的原子比較交換操作。 C ++ 11和C11引入了atomic_compare_exchange_strongatomic_compare_exchange_weak 允許后者虛假失敗。

通過允許try_lock虛假失敗,允許實現使用atomic_compare_exchange_weak來最大化性能並最小化代碼大小。

例如,在ARM64上,通常使用獨占加載( LDXR )和獨占存儲( STRX )指令來實現原子操作。 LDXR啟動“監視器”硬件,該硬件開始跟蹤對存儲器區域的所有訪問。 如果在LDXRSTRX指令之間沒有訪問該區域,則STRX僅執行存儲。 因此,如果另一個線程訪問該內存區域或者兩者之間存在IRQ,則整個序列可能會失敗。

在實踐中,使用弱保證實現的try_lock代碼生成與使用強保證實現的代碼沒有很大不同。

bool mutex_trylock_weak(atomic_int *mtx)
{
    int old = 0;
    return atomic_compare_exchange_weak(mtx, &old, 1);
}

bool mutex_trylock_strong(atomic_int *mtx)
{
    int old = 0;
    return atomic_compare_exchange_strong(mtx, &old, 1);
}

看看為ARM64生成的程序集:

mutex_trylock_weak:
  sub sp, sp, #16
  mov w1, 0
  str wzr, [sp, 12]
  ldaxr w3, [x0]      ; exclusive load (acquire)
  cmp w3, w1
  bne .L3
  mov w2, 1
  stlxr w4, w2, [x0]  ; exclusive store (release)
  cmp w4, 0           ; the only difference is here
.L3:
  cset w0, eq
  add sp, sp, 16
  ret

mutex_trylock_strong:
  sub sp, sp, #16
  mov w1, 0
  mov w2, 1
  str wzr, [sp, 12]
.L8:
  ldaxr w3, [x0]      ; exclusive load (acquire)
  cmp w3, w1
  bne .L9
  stlxr w4, w2, [x0]  ; exclusive store (release)
  cbnz w4, .L8        ; the only difference is here
.L9:
  cset w0, eq
  add sp, sp, 16
  ret

唯一的區別是“弱”版本消除了條件反向分支cbnz w4, .L8並用cmp w4, 0 cbnz w4, .L8替換它。 由於假設它們是循環的一部分,CPU將預測向后條件分支作為“將被采用”,因為它們被認為是循環的一部分 - 在這種情況下這種假設是錯誤的,因為大多數時間鎖定將被獲取(假設低爭用是最常見的情況)。

Imo這是這些功能之間唯一的性能差異。 在某些工作負載下,“強”版本在單指令上基本上會受到100%分支誤預測率的影響。

順便說一句,ARMv8.1引入了原子指令,因此兩者之間沒有區別,就像在x86_64上一樣。 -march=armv8.1-a生成的代碼-march=armv8.1-a標志:

  sub sp, sp, #16
  mov w1, 0
  mov w2, 1
  mov w3, w1
  str wzr, [sp, 12]
  casal w3, w2, [x0]
  cmp w3, w1
  cset w0, eq
  add sp, sp, 16
  ret

即使使用atomic_compare_exchange_strong ,某些try_lock函數也會失敗,例如, shared_mutex try_lock_shared可能需要增加讀取器計數器,如果另一個讀取器已進入鎖定,則可能會失敗。 這種函數的“強”變體需要生成循環,因此可能遭受類似的分支錯誤預測。

另一個小細節:如果互斥體是用C語言編寫的,那么一些編譯器(如Clang)可能會在16字節邊界處對齊循環以改善其性能,使用填充來增加函數體。 如果循環幾乎總是運行一次,這是不必要的。


虛假失敗的另一個原因是無法獲取內部互斥鎖(如果使用自旋鎖和某些內核原語實現互斥鎖)。 理論上,在try_lock的內核實現中可以獲得相同的原則,盡管這似乎不合理。

C ++並發內存模型第3部分的紙基礎中 ,已經清楚地解釋了為什么該標准允許try_lock虛假失敗。 簡而言之,它被指定使try_lock的語義與C ++內存模型中的種族定義一致。

暫無
暫無

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

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