volatile
#include <iostream>
#include <thread>
volatile int total{ 0 };
void func(int) {
for (int i = 0; i < 10000; ++i) {
total += i;
}
}
int main() {
std::thread t1(func, 0);
std::thread t2(func, 0);
t1.join();
t2.join();
std::cout << "total: " << total << std::endl;
return 0;
}
聲明某個變量的值是隨時可能被改變的,每次讀取次變量都從內存地址中直接讀取。
為了防止編譯器的優化而從寄存器中讀取數據,而導致多線程時數據不一致。
但是volatile僅僅是針對編譯器的,對CPU無影響,因此再多核環境下沒有任何作用。
- 與平台無關的多線程程序,volatile幾乎無用(JAVA的volatile除外,java的volatile有內存屏障指令)
- volatile不保證原子性(一般需使用CPU提供的LOCK指令)
- volatile不保證執行順序
- volatile不提供內存屏障和內存柵欄
- 多核環境中內存的可見性和CPU執行順序不能通過volatile來保障,而是以來於CPU的內存屏障
atomic
#include <iostream>
#include <thread>
#include <atomic>
std::atomic_int total{ 0 };
// 或 std::atomic<int> total{ 0 };
void func(int) {
for (int i = 0; i < 10000; ++i) {
total += i;
}
}
int main() {
std::thread t1(func, 0);
std::thread t2(func, 0);
t1.join();
t2.join();
std::cout << "total: " << total << std::endl;
return 0;
}
C++11中引入了atomic,使得程序相對mutex更加簡潔
編譯器或處理器可能會改變代碼的執行順序。
默認情況下C++11中的原子類型的變量在線程中總是保持着順序執行的特性(memory_order默認參數memory_order_seq_cst 全部存取都按照順序執行,非原子類型沒有必要,因為不需要在線程間同步)。我們稱這樣的特性為“順序一致”。
x86、SPARC(TSO模式)是強順序內存模型平台。
PowerPC、ArmV7 是弱內存模型構架,如果要保證指令執行的順序,通常需要有再匯編指令中加入一條所謂的內存柵欄(memory barrier)指令。如在PowerPC上,就有一條名為sync的內存柵欄指令,其對高度流水化的PowerPC處理器的性能影響很大。
如果在強順序內存模型平台(如x86)上,沒必要指定memory_order參數
如果在弱順序內存模型平台(如PowerPC)上,可以手動指定memory_order參數來提高性能
mutex
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> total{ 0 };
void func(int) {
for (int i = 0; i < 10000; ++i) {
int temp = total;
temp += i;
total = temp;
}
}
int main() {
std::thread t1(func, 0);
std::thread t2(func, 0);
t1.join();
t2.join();
std::cout << "total: " << total << std::endl;
return 0;
}
再上述的代碼中往往達不到我們想要的效果,因為atomic只能保證 int temp = total; 和 total = temp; 的原子性,但是如果再中間做了一些其他事,是不能保證原子性的,這個時候應該采用mutex
#include <iostream>
#include <thread>
#include <mutex>
int total{ 0 };
std::mutex total_mutex;
void func(int) {
for (int i = 0; i < 10000; ++i) {
std::lock_guard<std::mutex> lock(total_mutex);
int temp = total;
temp += i;
total = temp;
}
}
int main() {
std::thread t1(func, 0);
std::thread t2(func, 0);
t1.join();
t2.join();
std::cout << "total: " << total << std::endl;
return 0;
}
參考文章:《深入理解C++11》
https://www.cnblogs.com/tekkaman/p/10245341.html
https://blog.csdn.net/D_Guco/article/details/74826041?utm_source=blogxgwz5