技术

C++ 线程安全(Thread Safety)

中文English
一、线程安全的概念1. Concept of Thread Safety
线程安全指在多线程环境中,多个线程访问同一资源时,不会导致竞态条件、数据破坏或未定义行为。Thread safety means that in a multithreaded environment, multiple threads accessing the same resource will not cause race conditions, data corruption, or undefined behavior.
简单说,就是保证程序正确性和稳定性,即使多线程同时执行相关代码。In short, it guarantees program correctness and stability even when multiple threads execute related code simultaneously.
二、线程安全的级别2. Levels of Thread Safety
不安全(Not thread-safe):多线程同时访问会导致错误或竞态。Not thread-safe: concurrent access by multiple threads can cause errors or race conditions.
线程安全(Thread-safe):多线程访问不会产生竞态,行为正确。Thread-safe: multiple threads can access safely without race conditions and behave correctly.
可重入(Reentrant):函数可被多线程/中断安全调用,不依赖共享状态。Reentrant: functions can be safely called by multiple threads or interrupts, without relying on shared state.
线程局部(Thread-local):每个线程拥有独立变量副本,互不干扰。Thread-local: each thread has its own copy of variables, avoiding interference.
三、C++中实现线程安全的常用方法3. Common Ways to Achieve Thread Safety in C++
1. 互斥锁(Mutex):用std::mutex加锁,保证同一时刻只有一个线程访问共享资源。1. Mutex: use std::mutex to lock and ensure only one thread accesses shared resource at a time.
示例:Example:
“`cpp“`cpp
std::mutex mtx;std::mutex mtx;
int shared_data = 0;int shared_data = 0;
void safe_increment() {void safe_increment() {
std::lock_guardstd::mutex lock(mtx);std::lock_guardstd::mutex lock(mtx);
++shared_data;++shared_data;
}}
“`“`
2. 读写锁(Shared Mutex):允许多个线程读,写时独占锁,C++17 提供std::shared_mutex。2. Shared Mutex: allows multiple readers concurrently but writer has exclusive lock, provided by C++17 as std::shared_mutex.
3. 原子操作(Atomic):使用std::atomic实现无锁同步,适合简单类型变量。3. Atomic: use std::atomic for lock-free synchronization, suitable for simple types.
示例:Example:
“`cpp“`cpp
std::atomic<int> count(0);std::atomic<int> count(0);
void increment() {void increment() {
count.fetch_add(1, std::memory_order_relaxed);count.fetch_add(1, std::memory_order_relaxed);
}}
“`“`
4. 线程局部存储(Thread Local):用thread_local关键字为每线程分配独立变量。4. Thread Local Storage: use thread_local keyword to assign each thread independent variables.
5. 不可变数据/只读数据:共享只读数据天然线程安全。5. Immutable / Read-only data: shared read-only data is naturally thread-safe.
四、线程安全的盲点与陷阱4. Pitfalls and Common Problems in Thread Safety
1. 竞态条件(Race Condition):多线程同时修改共享数据,导致不可预测结果。1. Race Condition: concurrent modification causes unpredictable results.
示例见下。See example below.
2. 死锁(Deadlock):多个线程互相等待对方持有的锁,导致程序卡死。2. Deadlock: threads wait on each other’s locks causing indefinite blocking.
示例见下。See example below.
3. 内存可见性(Memory Visibility):一个线程修改变量,另一个线程可能看不到最新值。3. Memory Visibility: changes in one thread may not be visible to another thread immediately.
示例见下。See example below.
4. 锁的粒度(Lock Granularity):锁范围过大导致性能瓶颈,过小则增加复杂度。4. Lock Granularity: coarse locks cause performance bottlenecks; fine locks add complexity.
示例见下。See example below.
5. 读写不一致(Read-Write Inconsistency):读写顺序未同步导致数据错误。5. Read-Write Inconsistency: unsynchronized read/write order leads to incorrect data.
示例见下。See example below.
五、盲点示例代码5. Example Code for Pitfalls
竞态条件(Race Condition)示例:Race Condition Example:
“`cpp“`cpp
int counter = 0;int counter = 0;
void unsafe_increment() {void unsafe_increment() {
for(int i = 0; i < 1000; ++i) ++counter;for(int i = 0; i < 1000; ++i) ++counter;
}}
int main() {int main() {
std::thread t1(unsafe_increment);std::thread t1(unsafe_increment);
std::thread t2(unsafe_increment);std::thread t2(unsafe_increment);
t1.join();t1.join();
t2.join();t2.join();
std::cout << “最终计数: ” << counter << std::endl;std::cout << “Final count: ” << counter << std::endl;
return 0;return 0;
}}
“`“`
死锁(Deadlock)示例:Deadlock Example:
“`cpp“`cpp
std::mutex mtx1, mtx2;std::mutex mtx1, mtx2;
void threadA() {void threadA() {
std::lock_guardstd::mutex lock1(mtx1);std::lock_guardstd::mutex lock1(mtx1);
std::this_thread::sleep_for(std::chrono::milliseconds(100));std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::lock_guardstd::mutex lock2(mtx2);std::lock_guardstd::mutex lock2(mtx2);
std::cout << “线程A完成\n”;std::cout << “Thread A done\n”;
}}
void threadB() {void threadB() {
std::lock_guardstd::mutex lock2(mtx2);std::lock_guardstd::mutex lock2(mtx2);
std::this_thread::sleep_for(std::chrono::milliseconds(100));std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::lock_guardstd::mutex lock1(mtx1);std::lock_guardstd::mutex lock1(mtx1);
std::cout << “线程B完成\n”;std::cout << “Thread B done\n”;
}}
int main() {int main() {
std::thread t1(threadA);std::thread t1(threadA);
std::thread t2(threadB);std::thread t2(threadB);
t1.join();t1.join();
t2.join();t2.join();
return 0;return 0;
}}
“`“`
内存可见性(Memory Visibility)示例:Memory Visibility Example:
“`cpp“`cpp
bool ready = false;bool ready = false;
int data = 0;int data = 0;
void producer() {void producer() {
data = 42;data = 42;
ready = true;ready = true;
}}
void consumer() {void consumer() {
while (!ready) {while (!ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));