中文 | 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)); | |