c++++多线程同步通过多种机制确保线程安全;1.互斥锁(mutex)用于保护共享资源,如代码中使用mtx.lock()和mtx.unlock()控制counter访问;2.条件变量(condition variable)用于线程等待特定条件,如cv.wait()和cv.notify_one()配合unique_lock实现线程通信;3.原子操作(atomic operations)提供轻量级同步,如std::atomic保证counter++的原子性;4.读写锁(read-write lock)允许多个线程同时读取,如std::shared_mutex配合shared_lock和unique_lock实现读写控制;5.信号量(semaphore)控制资源访问数量,如std::counting_semaphore管理最多三个并发线程。选择时应根据场景:互斥锁适合保护共享数据,条件变量适合等待条件触发,原子操作适合简单计数器,读写锁适合读多写少,信号量适合资源池管理。避免死锁的方法包括避免嵌套锁、使用超时锁、减小锁粒度、资源排序等。c++11后新增了recursive_mutex、timed_mutex、future/promise等工具提升并发编程能力。原子操作适用于简单操作,互斥锁则更适合复杂资源保护。
C++多线程同步,简单来说,就是让多个线程能够安全地共享资源,避免出现数据竞争和死锁等问题。要做到这一点,你需要用到一些同步机制,比如互斥锁、条件变量、原子操作等等。
互斥锁(Mutex) 最常用的同步机制之一。想象一下,你和你的朋友想同时用一支笔,但笔只有一个,这时候就需要互斥锁。
#include <iostream> #include <thread> #include <mutex> std::mutex mtx; // 定义一个互斥锁 int counter = 0; void increment() { for (int i = 0; i < 10000; ++i) { mtx.lock(); // 加锁 counter++; mtx.unlock(); // 解锁 } } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Counter value: " << counter << std::endl; // 预期结果:20000 return 0; }
这段代码里,mtx.lock() 就像是你拿起了笔,mtx.unlock() 就像是你用完放下了笔。只有拿到锁的线程才能访问 counter 变量。
条件变量(Condition Variable)
立即学习“C++免费学习笔记(深入)”;
互斥锁可以保证资源的安全访问,但如果线程需要等待某个特定条件满足才能继续执行,互斥锁就不够用了。这时候就需要条件变量。
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; bool ready = false; void worker_thread() { std::unique_lock<std::mutex> lock(mtx); // 自动解锁的互斥锁 cv.wait(lock, []{ return ready; }); // 等待条件满足 std::cout << "Worker thread is processing..." << std::endl; } void signal_ready() { std::this_thread::sleep_for(std::chrono::seconds(1)); { std::lock_guard<std::mutex> lock(mtx); ready = true; } cv.notify_one(); // 通知一个等待的线程 } int main() { std::thread worker(worker_thread); std::thread signaler(signal_ready); worker.join(); signaler.join(); return 0; }
cv.wait(lock, []{ return ready; }) 让线程进入等待状态,直到 ready 变为 true。cv.notify_one() 用于唤醒一个等待的线程。 std::unique_lock 是一个RAII风格的锁,离开作用域会自动解锁,避免忘记解锁导致死锁。
原子操作(Atomic Operations)
原子操作是一种更轻量级的同步机制,适用于简单的计数器或者标志位。原子操作保证操作的原子性,即不可分割。
#include <iostream> #include <thread> #include <atomic> std::atomic<int> counter(0); void increment() { for (int i = 0; i < 10000; ++i) { counter++; // 原子操作 } } int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Counter value: " << counter << std::endl; // 预期结果:20000 return 0; }
std::atomic
读写锁(Read-Write Lock)
读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这种锁适用于读多写少的场景。C++标准库并没有直接提供读写锁,但你可以使用第三方库,例如 Boost。
#include <iostream> #include <thread> #include <shared_mutex> // C++17引入 std::shared_mutex rw_mutex; int data = 0; void reader() { for (int i = 0; i < 5; ++i) { std::shared_lock<std::shared_mutex> lock(rw_mutex); // 共享锁 std::cout << "Reader: " << data << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } void writer() { for (int i = 0; i < 3; ++i) { std::unique_lock<std::shared_mutex> lock(rw_mutex); // 独占锁 data++; std::cout << "Writer: " << data << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(200)); } } int main() { std::thread t1(reader); std::thread t2(writer); std::thread t3(reader); t1.join(); t2.join(); t3.join(); return 0; }
std::shared_lock 用于获取共享锁,允许多个线程同时读取数据。std::unique_lock 用于获取独占锁,只允许一个线程写入数据。C++17 引入了 std::shared_mutex,简化了读写锁的使用。
信号量(Semaphore)
信号量是一种更通用的同步机制,可以控制对有限资源的访问。
#include <iostream> #include <thread> #include <semaphore> std::counting_semaphore<3> semaphore(3); // 允许最多3个线程同时访问 void worker(int id) { semaphore.acquire(); // 获取信号量 std::cout << "Thread " << id << " is working..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "Thread " << id << " is done." << std::endl; semaphore.release(); // 释放信号量 } int main() { std::thread t1(worker, 1); std::thread t2(worker, 2); std::thread t3(worker, 3); std::thread t4(worker, 4); std::thread t5(worker, 5); t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); return 0; }
std::counting_semaphore semaphore(3) 定义了一个初始值为 3 的信号量。semaphore.acquire() 用于获取信号量,如果信号量的值为 0,线程将进入等待状态。semaphore.release() 用于释放信号量,使信号量的值加 1。
选择合适的同步机制取决于你的具体需求。
多线程同步虽然能解决并发访问的问题,但也可能引入一些新的问题。
死锁是多线程编程中最常见的问题之一,以下是一些避免死锁的常用方法:
C++11 引入了许多新的同步工具,例如:
原子操作通常比互斥锁更轻量级,性能更高,但原子操作只能用于简单的操作,例如计数器或者标志位的更新。如果需要保护复杂的共享资源,或者需要执行多个操作的原子性,应该使用互斥锁。
总的来说,选择合适的同步机制,并正确地使用它们,是编写高效、安全的并发程序的关键。
以上就是C++怎么使用多线程同步 C++多线程同步的几种机制对比的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号