3 回答

TA貢獻1784條經(jīng)驗 獲得超2個贊
.現(xiàn)在讓我們啟用優(yōu)化:
f():
rep ret
好吧,讓我們給它一個機會:
void f(int& num)
{
num = 0;
num++;
--num;
num += 6;
num -=5;
--num;
}
結果:
f(int&):
mov DWORD PTR [rdi], 0
ret
另一個觀察線程(甚至忽略緩存同步延遲)沒有機會觀察單個更改。
與之相比:
#include <atomic>
void f(std::atomic<int>& num)
{
num = 0;
num++;
--num;
num += 6;
num -=5;
--num;
}
其結果是:
f(std::atomic<int>&):
mov DWORD PTR [rdi], 0
mfence
lock add DWORD PTR [rdi], 1
lock sub DWORD PTR [rdi], 1
lock add DWORD PTR [rdi], 6
lock sub DWORD PTR [rdi], 5
lock sub DWORD PTR [rdi], 1
ret
現(xiàn)在,每項修改都是:
在另一個線程中可以觀察到,并且
尊重發(fā)生在其他線程中的類似修改。
原子性不只是在指令級,它涉及到從處理器到緩存,到內(nèi)存和返回的整個管道。
進一步信息
的更新的優(yōu)化效果std::atomicS.
c+標準具有“似乎”規(guī)則,允許編譯器重新排序代碼,甚至可以重寫代碼,條件是結果具有完全相同效果(包括副作用),就好像它只是簡單地執(zhí)行了您的代碼。
如果規(guī)則是保守的,特別涉及原子。
考慮:
void incdec(int& num) {
++num;
--num;
}
由于沒有互斥鎖、Atomics或任何其他影響線程間排序的構造,我認為編譯器可以自由地將此函數(shù)重寫為NOP,例如:
void incdec(int&) {
// nada
}
這是因為在c+內(nèi)存模型中,不可能有另一個線程觀察增量的結果。當然,如果num曾.volatile(可能會影響硬件行為)。但是在這種情況下,這個函數(shù)將是唯一修改這個內(nèi)存的函數(shù)(否則程序是格式錯誤的)。
然而,這是一場不同的球賽:
void incdec(std::atomic<int>& num) {
++num;
--num;
}
num是原子。對它的改變必觀察其他正在觀察的線索。更改這些線程本身的值(例如在增量和減少之間將值設置為100)將對num的最終值產(chǎn)生非常深遠的影響。
下面是一個演示:
#include <thread>
#include <atomic>
int main()
{
for (int iter = 0 ; iter < 20 ; ++iter)
{
std::atomic<int> num = { 0 };
std::thread t1([&] {
for (int i = 0 ; i < 10000000 ; ++i)
{
++num;
--num;
}
});
std::thread t2([&] {
for (int i = 0 ; i < 10000000 ; ++i)
{
num = 100;
}
});
t2.join();
t1.join();
std::cout << num << std::endl;
}
}
樣本輸出:
99
99
99
99
99
100
99
99
100
100
100
100
99
99
100
99
99
100
100
99

TA貢獻1859條經(jīng)驗 獲得超6個贊
add DWORD PTR [rbp-4], 1
AGENT 1 AGENT 2load X inc C load X inc C store X store X
- 3 回答
- 0 關注
- 774 瀏覽
添加回答
舉報