函数调用本身已是内存屏障,为何pthread_mutex_lock()等还要内置?
pthread_mutex_lock()/pthread_mutex_unlock() Include Built-in Memory Barriers? Great question! The value of those built-in memory barriers doesn’t show up in a single-threaded snippet like yours—but it’s critical when you have multiple threads interacting. Let’s break this down step by step.
First, let’s clarify a common misconception: while function calls do enforce some order within a single thread, they don’t prevent compiler/CPU reordering across function boundaries, nor do they guarantee visibility of changes between threads. That’s where the mutex’s memory barriers come in.
What Do the Mutex’s Memory Barriers Actually Do?
pthread_mutex_lock() acts as an acquire barrier, and pthread_mutex_unlock() acts as a release barrier:
- Acquire barrier (lock):
- Prevents any subsequent read/write operations from being reordered before the lock is acquired.
- Ensures that all changes made by other threads (and released via
unlock()on the same mutex) are visible to the current thread once the lock is held.
- Release barrier (unlock):
- Prevents any preceding read/write operations from being reordered after the lock is released.
- Ensures that all changes made by the current thread before the
unlock()are visible to other threads that later acquire the same mutex.
Your Single-Threaded Example vs. a Multi-Threaded Scenario
In your single-threaded code:
a = 5; b = 7; pthread_mutex_lock(&lock); i++; pthread_mutex_unlock(&lock); c = 10;
It’s true that you won’t see a difference with or without the memory barriers—because the single thread’s execution order is already guaranteed. But let’s add a second thread to see why the barriers matter:
Thread 1 (your original code):
a = 5; b = 7; pthread_mutex_lock(&lock); i++; pthread_mutex_unlock(&lock); c = 10;
Thread 2:
pthread_mutex_lock(&lock); printf("a=%d, b=%d, i=%d\n", a, b, i); // Expect: a=5, b=7, i=1 pthread_mutex_unlock(&lock); printf("c=%d\n", c); // Might be 0 or 10, depending on timing
Without the mutex’s memory barriers:
- The compiler could reorder
a=5andb=7to after thelock()call (since in a single thread, this doesn’t change behavior). Thread 2 might then seei=1buta/bstill holding their old values—total chaos. - Or, the compiler could reorder
c=10to before theunlock()call. Thread 2 might seec=10even before Thread 1 has finished incrementingi, which breaks your intended logic.
With the built-in memory barriers:
lock()ensures Thread 2 seesa=5andb=7(from Thread 1) before accessingi.unlock()ensures Thread 1’si++is fully visible to Thread 2 when it acquires the lock, and thatc=10doesn’t happen until after the mutex is released.
Why “Function Calls as Barriers” Isn’t Enough
When people say “function calls act as memory barriers,” they usually mean that the compiler won’t reorder code inside the function with code outside in a way that breaks the function’s logic—but that’s a weak guarantee. It doesn’t prevent the kind of cross-thread visibility issues or aggressive reordering that mutex barriers block. The mutex’s barriers are strict, hardware-aware guarantees that ensure your multi-threaded code behaves as you’d logically expect.
内容的提问来源于stack exchange,提问作者James

