在學習多執行緒編程時,我撰寫了以下代碼。
#include <thread>
#include <iostream>
#include <cassert>
void check() {
int a = 0;
int b = 0;
{
std::jthread t2([&](){
int i = 0;
while (a >= b) {
i;
}
std::cout << "failed at iteration " << i << "\n"
// I know at this point a and b may have changed
<< a << " >= " << b << "\n";
std::exit(0);
});
std::jthread t1([&](){
while (true) {
a;
b;
}
});
}
}
int main() {
check();
}
由于 a總是發生在 ba 應該總是大于或等于 b 之前。但實驗表明,有時b > a. 為什么?是什么原因造成的?我該如何強制執行?
即使我替換int a = 0;為int a = 1000;which 使所有這一切變得更加瘋狂。
程式很快退出,因此不會發生 int 溢位。我沒有發現可能導致這種情況的匯編中重新排序的說明。
uj5u.com熱心網友回復:
因為 a 總是在 ba 之前發生,所以應該總是大于或等于 b
僅在其執行執行緒中。并且僅當執行執行緒可以觀察到時。
C 需要某些顯式的“同步”,以便其他執行執行緒可以看到一個執行執行緒所做的更改。
a;
b;
僅憑這些陳述句,該執行執行緒實際上無法“區分”是先遞增a還是b先遞增。因此,C 允許編譯器實作它想要的任何優化或代碼重新排序步驟,只要它在其執行執行緒中沒有可觀察到的效果,并且如果實際生成的代碼b首先遞增,則不會有任何可觀察到的效果。這個執行執行緒不可能分辨出差異。
但是,如果有一些中間陳述句“查看”了a,那么這將不再成立,并且編譯器將需要a在以某種方式使用它之前實際生成遞增的代碼。
這只是這個執行執行緒,單獨存在。即使可以觀察到a在b此執行執行緒中更改的相對順序,根據標準,C 編譯器也被允許以任何順序實際遞增實際變數,只要還有任何其他調整使此不可觀察。但它可以被另一個執行執行緒觀察到。為了防止這種情況發生,有必要采取顯式同步步驟,使用互斥鎖、條件變數和 C 執行執行緒模型的其他部分。
uj5u.com熱心網友回復:
在這些不同變數的增量和讀取它們之間存在非平凡的競爭條件。如果您想對這些讀取和寫入進行嚴格排序,您將不得不使用某種同步機制。std::atomic<> 使它更容易。
試試這個:
#include<iostream>
#include <thread>
#include <iostream>
#include <cassert>
#include <atomic>
void check() {
struct AB { int a = 0; int b=0; };
std::atomic<AB> ab;
{
std::jthread t2([&](){
int i = 0;
AB temp;
while (true) {
temp = ab;
if ( temp.a > temp.b ) break;
i;
}
std::cout << "failed at iteration " << i << "\n"
// I know at this point a and b may have changed
<< temp.a << " >= " << temp.b << "\n";
std::exit(0);
});
std::jthread t1([&](){
while (true) {
AB temp = ab;
temp.a ;
temp.b ;
ab = temp;
}
});
}
}
int main() {
check();
}
代碼:https : //godbolt.org/z/Kxeb8d8or
結果:
Program returned: 143
Program stderr
Killed - processing time exceeded
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/391650.html
上一篇:WinUI3運行時本地化
下一篇:如何在C#中原子地執行代碼塊
