C++ 互斥锁、条件变量的基础使用
前言
在C++多线程开发中,互斥锁和条件变量是线程同步中非常重要的部分。互斥锁和条件变量主要用于确保对共享资源的访问是线程安全的(C++的容器都不是线程安全的)。本文主要介绍互斥锁和条件变量的基础使用。
互斥锁(Mutex)
互斥锁用于保护共享数据,防止多个线程同时修改同一资源;
当在多线程场景时,某一线程访问某一个被互斥锁保护的资源时,线程会首先尝试获取该互斥锁的所有权,但是如果锁已经被其他线程获取所有权,则该线程会被阻塞,直到其他线程放弃锁的所有权。
在C++中,<mutex>
头文件提供了std::mutex
类
示例代码
#include <mutex>
#include <thread>
std::mutex mtx; // 创建一个互斥锁
void func()
{ mtx.lock(); // 尝试获取锁 // 访问或修改共享资源 mtx.unlock(); // 释放锁
} // 或者使用std::lock_guard简化锁的获取与释放
void func_with_guard()
{ std::lock_guard<std::mutex> lock(mtx); // 构造时自动获取锁,析构时自动释放锁 // 访问或修改共享资源
}
条件变量(Condition Variable)
条件变量主要与互斥锁配合使用,能够将线程在某一条件不满足时,使其挂起,并在条件满足时唤醒;
在C++中,<condition_variable>
头文件提供了
std::condition_variable
std::condition_variable_any
两者对比,后者相对前者更灵活,但是性能相对差一点;
示例代码
#include <mutex>
#include <condition_variable>
#include <thread> std::mutex mtx;
std::condition_variable cv;
bool ready = false; void print_id(int id)
{ std::unique_lock<std::mutex> lck(mtx); while (!ready) {cv.wait(lck); // 等待ready变为true }// 当ready为true时,继续执行... std::cout << "Thread " << id << '\n';
} void go()
{ std::unique_lock<std::mutex> lck(mtx); ready = true; cv.notify_all(); // 唤醒所有等待的线程
} int main()
{ std::thread threads[10]; // 创建10个线程 for (int i=0; i<10; ++i) {threads[i] = std::thread(print_id, i);} std::cout << "10 threads ready to race...\n";go(); // 唤醒所有线程 // 等待所有线程完成 for (auto& th : threads) {th.join(); return 0; }
}
条件变量相关函数
线程阻塞wait
;
基本形式:
void wait(std::unique_lock<std::mutex>& lock);
wait函数接受一个unique_lock<std::mutex>
类型的引用作为参数;调用wait
函数时,当前线程会释放被 unique_lock<std::mutex>
占用的锁并让线程阻塞,直到被另一个线程唤醒;
线程唤醒
notify_one
void notify_all() noexcept;
唤醒在条件变量上等待队列
中的第一个线程;
(如果队列为空,不会有任何效果)
notify_all
void notify_one() noexcept;
唤醒等待队列中的所有线程;
相同点
当线程被唤醒时,会尝试获取与条件变量相关联的互斥锁。然而,由于只有一个线程能在任何给定时间持有锁,因此只有一个线程会成功获取锁并从wait
函数中返回。未成功获取锁的线程将重新进入等待状态,直到锁被释放并再次尝试获取。