
要编写高效且正确的多线程应用程序,了解执行线程之间存在哪些内存同步机制,多线程编程元素(例如互斥锁,联接线程等)提供哪些保证非常重要。 C ++内存模型尤其如此,该模型设计得很复杂,可以为许多处理器体系结构提供最佳的多线程代码。顺便说一句,建立在LLVM上的Rust编程语言使用与C ++中相同的内存模型。因此,本文中的材料对于使用两种语言的程序员都是有用的。但是所有示例都将使用C ++。我将讨论std::atomic,std::memory_order以及在哪三个大象上是原子。
C++11 C++, . . , . . , , . - ( ). , , : , . . , , . - . x86-64 ARM , .
C++ , ++11 , , .
: C++ — "" , . C++ , undefined behavior (UB), , .
, C++, , . , .
, . (std::atomic), .. "" . , (std::mutex) , , . , .
, C++ , . ?
… .
.
.
— , , . . std::atomic, : load, store, fetch_add, compare_exchange_* . — read-modify-write , .
read-modify-write , . 0, link:
static int v1 = 0;
static std::atomic<int> v2{ 0 };
int add_v1() {
return ++v1;
/* Generated x86-64 assembly:
mov eax, DWORD PTR v1[rip]
add eax, 1
mov DWORD PTR v1[rip], eax
*/
}
int add_v2() {
return v2.fetch_add(1);
/* Generated x86-64 assembly:
mov eax, 1
lock xadd DWORD PTR _ZL2v2[rip], eax
*/
} v1 int : read-modify-write. , v1. v2 lock , , , v2, , .
. , , . . . , , . .
. , , . , , , , . UB.
, :
, ,
, . C++ . : relaxed, release/acquire sequential consistency. .
,
— relaxed. , . :
""
thread2"" ,thread1thread1thread2
relaxed . 1, link:
std::atomic<size_t> counter{ 0 };
// process can be called from different threads
void process(Request req) {
counter.fetch_add(1, std::memory_order_relaxed);
// ...
}
void print_metrics() {
std::cout << "Number of requests = " << counter.load(std::memory_order_relaxed) << "\n";
// ...
}. 2, link:
std::atomic<bool> stopped{ false };
void thread1() {
while (!stopped.load(std::memory_order_relaxed)) {
// ...
}
}
void stop_thread1() {
stopped.store(true, std::memory_order_relaxed);
} thread1 , stop_thread1. , thread1 () stopped true.
relaxed . 3, link:
std::string data;
std::atomic<bool> ready{ false };
void thread1() {
data = "very important bytes";
ready.store(true, std::memory_order_relaxed);
}
void thread2() {
while (!ready.load(std::memory_order_relaxed));
std::cout << "data is ready: " << data << "\n"; // potentially memory corruption is here
} , thread2 data , ready, .. relaxed .
" " (sequential consistency, seq_cst) . :
thread1thread2.
( )
thread1,store,loadthread2
seq_cst , , .
C++ , .. . seq_cst , . , x86-64 seq_cst , ARM .
. 4, [1], link:
std::atomic<bool> x, y;
std::atomic<int> z;
void thread_write_x() {
x.store(true, std::memory_order_seq_cst);
}
void thread_write_y() {
y.store(true, std::memory_order_seq_cst);
}
void thread_read_x_then_y() {
while (!x.load(std::memory_order_seq_cst));
if (y.load(std::memory_order_seq_cst)) {
++z;
}
}
void thread_read_y_then_x() {
while (!y.load(std::memory_order_seq_cst));
if (x.load(std::memory_order_seq_cst)) {
++z;
}
} , , z 1 2, thread_read_x_then_y thread_read_y_then_x "" x y . : x = true, y = true, y = true, x = true.
seq_cst relaxed acquire/release, . seq_cst , : seq_cst . 1 2 , relaxed seq_cst, 3 .
. Acquire/Release
acquire/release . : memory_order_acquire memory_order_release . :
release,acquirethread1,release,acquirethread2releasethread1,acquirethread2
, , . , 4 store memory_order_release, load memory_order_acquire, z 0, 1 2. , , store x y, thread_read_x_then_y thread_read_y_then_x . , load store 3. , .. ( seq_cst ), .
release, , . acquire, "" , . release acquire , UB .
, , lock. spinlock. , , . 5, link:
class mutex {
public:
void lock() {
bool expected = false;
while(!_locked.compare_exchange_weak(expected, true, std::memory_order_acquire)) {
expected = false;
}
}
void unlock() {
_locked.store(false, std::memory_order_release);
}
private:
std::atomic<bool> _locked;
}; lock() false true acquire. compare_exchage_weak strong , cppreference. unlock() false release. , , . , unlock() , lock(). . , .
, Double Checked Locking Anti-Pattern [2]. 6, link:
struct Singleton {
// ...
};
static Singleton* singleton = nullptr;
static std::mutex mtx;
static bool initialized = false;
void lazy_init() {
if (initialized) // early return to avoid touching mutex every call
return;
std::unique_lock l(mtx); // `mutex` locks here (acquire memory)
if (!initialized) {
singleton = new Singleton();
initialized = true;
}
// `mutex` unlocks here (release memory)
} : Singleton. , . .. , singleton read-only , if (initialized) return. , x86-64. C++. :
void thread1() {
lazy_init();
singleton->do_job();
}
void thread2() {
lazy_init();
singleton->do_job();
}:
1. thread1 -> :
lock (
acquire)singleton = ..initialized = trueunlock (
release)
2. thread2:
if(initalized)true(,initialized)singleton->do_job()segmentation fault(singletonthread1)
, , .
acquire/release
acquire/release , . .
| |
| |
| lock , unlock. |
|
|
. [1].
? : , std::promise::set_value std::future::wait, , , , , set_value. , -, . , , , , , .
C++ , . , . , , C++. volatile bool, , , read-modify-write , . , . , !
[1] Anthony Williams. C++ Concurrency in Action. https://www.amazon.com/C-Concurrency-Action-Practical-Multithreading/dp/1933988770
[2]托尼·范·埃德(Tony van Eerd)。C ++内存模型和无锁编程。https://www.youtube.com/watch?v=14ntPfyNaKE