线程同步方式有哪些?
线程同步机制是指在多线程编程中,为了保证线程之间的互不干扰,而采用的一种机制。常见的线程同步机制有以下几种:
互斥锁:互斥锁是最常见的线程同步机制。它允许只有一个线程同时访问被保护的临界区(共享资源)
条件变量:条件变量用于线程间通信,允许一个线程等待某个条件满足,而其他线程可以发出信号通知等待线程。通常与互斥锁一起使用。
读写锁:
读写锁允许多个线程同时读取共享资源,但只允许一个线程写入资源。
信号量:用于控制多个线程对共享资源进行访问的工具。
1. 互斥锁 (Mutex - Mutual
Exclusion)
原理 :像一把钥匙,只允许一个线程进入被保护的代码段(临界区)。其他线程必须等待该线程释放锁。
类比 :一个单人的卫生间,一个人进去后从里面锁门,其他人只能在门口等待。
示例(C++ std::mutex
):
#include <iostream> #include <thread> #include <mutex> std::mutex mtx; int shared_data = 0 ;void increment () { for (int i = 0 ; i < 100000 ; ++i) { mtx.lock (); ++shared_data; mtx.unlock (); } } int main () { std::thread t1 (increment) ; std::thread t2 (increment) ; t1. join (); t2. join (); std::cout << "Final value: " << shared_data << std::endl; return 0 ; }
更安全的用法(std::lock_guard
):
void increment_safe () { for (int i = 0 ; i < 100000 ; ++i) { std::lock_guard<std::mutex> lock (mtx) ; ++shared_data; } }
2. 条件变量 (Condition
Variable)
原理 :允许线程在某个条件不满足时主动等待,直到其他线程改变条件并通知它。必须与互斥锁配合使用 。
类比 :顾客在餐厅等空位。没有空位时(条件不满足),顾客就等待。服务员有空位时(改变条件),通知等待的顾客。
示例(C++
std::condition_variable
):
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; bool is_ready = false ; void consumer () { std::unique_lock<std::mutex> lock (mtx) ; cv.wait (lock, []{ return is_ready; }); std::cout << "Data is ready!\n" ; } void producer () { std::this_thread::sleep_for (std::chrono::seconds (1 )); { std::lock_guard<std::mutex> lock (mtx) ; is_ready = true ; } cv.notify_one (); } int main () { std::thread t1 (consumer) ; std::thread t2 (producer) ; t1. join (); t2. join (); return 0 ; }
3. 读写锁 (Read-Write Lock)
原理 :允许多个线程同时读,但只允许一个线程写。写操作时,禁止所有读操作。
类比 :公司的公告板。很多员工可以同时看(读),但只有行政人员可以修改内容(写),且修改时不允许其他人看。
示例(C++ std::shared_mutex
C++17):
#include <iostream> #include <thread> #include <shared_mutex> std::shared_mutex rw_mtx; int shared_value = 0 ;void reader (int id) { std::shared_lock<std::shared_mutex> lock (rw_mtx) ; std::cout << "Reader " << id << " sees value: " << shared_value << std::endl; } void writer (int id) { std::unique_lock<std::shared_mutex> lock (rw_mutex) ; ++shared_value; std::cout << "Writer " << id << " updated value to: " << shared_value << std::endl; } int main () { std::thread readers[10 ]; std::thread writers[2 ]; for (int i = 0 ; i < 2 ; ++i) writers[i] = std::thread (writer, i); for (int i = 0 ; i < 10 ; ++i) readers[i] = std::thread (reader, i); for (auto & t : readers) t.join (); for (auto & t : writers) t.join (); return 0 ; }
4. 信号量 (Semaphore)
原理 :维护一个计数器,控制同时访问某个资源的线程数量。P
操作(wait)减少计数,V
操作(signal)增加计数。
类比 :停车场门口的剩余车位计数器。车开进去(P操作),车位减一。车开出来(V操作),车位加一。计数器为0时,栏杆不放行。
注意 :C++标准库没有直接提供信号量,但可以用互斥锁和条件变量实现,或者在C++20中使用
std::counting_semaphore
。
示例(概念性):
class Semaphore {public : Semaphore (int count) : count_ (count) {} void Wait () { std::unique_lock<std::mutex> lock (mutex_) ; cv_.wait (lock, [this ] { return count_ > 0 ; }); --count_; } void Signal () { std::lock_guard<std::mutex> lock (mutex_) ; ++count_; cv_.notify_one (); } private : int count_; std::mutex mutex_; std::condition_variable cv_; }; Semaphore db_semaphore (5 ) ;void access_database () { db_semaphore.Wait (); db_semaphore.Signal (); }