📄前言
在C++线程库推出之前,如果要实现跨平台多线程,那么我们就得需要直到每个平台的线程API的知识,这无疑对每个程序员都是不小的挑战,毕竟大部分人都不愿意去一一学习这些接口,而线程库的推出,封装了线程底层的实现,不仅提高了代码的可移植性,还减少了C++的学习压力。
C++线程库
创建线程
thread是c++11引进的线程管理机制,主要用于封装系统底层的线程API, 例如windows的 CreateThread 和linux的pthread_create,从而提高代码的可移植性。
- thread的函数原型与参数
template <class Function, class... Args>
explicit thread (Function&& f, Args&&... args);
// 创建一个线程,可用lamda、函数指针+参数等形式
id get_id() const noexcept;
bool joinable() const noexcept;
- 成员函数
- get_id:返回线程id
- joinable:检查一个线程是否可以被回收。
- join:回收线程的资源,防止僵死线程,如果线程没有执行完,则阻塞等待。
- detach:与主线程分离,线程执行完毕后自动回收资源。
函数使用示例:
void func(std::string str)
{
std::cout << std::this_thread::get_id() << std::endl;
for(int i = 0; i < 10; i++)
{
std::cout << str << ":" << i << std::endl;
}
}
int main() {
std::thread t1(func, "thread 1");
std::thread t2([]() { func("thread 2"); });
t1.join();
t2.join();
return 0;
}
互斥量
mutex
mutex 是c++线程库最基础的互斥机制,用于保护临界资源不被多个线程同时访问,mutex 只提供了基础的锁和解锁接口。
class mutex {
public:
lock();
unlock();
try_lock();
}
- 成员函数:
- lock:锁定互斥量,只允许一个线程访问,
- unlock:解锁互斥量,
- try_lock:尝试锁定互斥量,如果互斥量被锁定则不阻塞,一直检查锁是否解锁(返回0),直到锁被解锁(返回1)。
lock_guard
lock_guard 与互斥量一起使用,提供一种RAII风格的管理机制(析构时自动调用unlock),避免忘记解锁而导致死锁的情况。
template <class Mutex> class lock_guard;
unique_lock
unique_lock 的基础用法与lock_guard一样,但比它提供了更多的功能,如控制锁的时间、方式,对条件变量的支持,以及可被移动(不可被复制)
template <class Mutex> class unique_lock;
template <class Clock, class Duration>
bool try_lock_until (const chrono::time_point<Clock,Duration>& abs_time);
template <class Rep, class Period>
bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);
// 复制构造
unique_lock (const unique_lock&) = delete;
// 移动构造
unique_lock (unique_lock&& x);
- 常用成员函数:
- try_lock:不阻塞地申请锁
- try_lock_for:与chrono类一起使用,等待指定的时间后去申请锁,申请失败继续等待。
- try_lock_until:在指定的时间到底前都能一直申请锁,时间到达但没获得锁,则继续执行其他任务。
函数使用示例:
void fun_c(std::string name, int n)
{
std::unique_lock<std::timed_mutex> lock(t_mtx, std::defer_lock);
if(lock.try_lock_for(std::chrono::seconds(n)))
{
std::cout << name << " ";
for(int i = 0; i < 10; ++i)
{
std::unique_lock<std::mutex> lock(mtx);
std::cout << counter++ << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(6));
}
else
std::cout << "can't lock" << std::endl;
std::cout << name << "end" << std::endl;
}
int main() {
std::thread t1(fun_c, "thread 1:", 2);
std::thread t2(fun_c, "thread 2:", 2);
t1.join();
t2.join();
return 0;
}
同步机制
condition_variable
condition_variable 是对系统条件变量的包装,用于实现线程直接的等待通知机制,需要与互斥量一起使用,拥有两大功能 wait 和 notify。
class condition_variable;
- 成员函数:
- wait:线程在这个函数中等待,直到接受到信号通知,和unique_lock 一样,它也有wait_for 和 wait_until,需要与互斥量一起使用。线程等待时会将锁给解锁。
- notify_once:通知一个等待着的线程,并唤醒它。
- notify_all:通知所有在wait等待着的线程,唤醒它们。
函数使用
std::condition_variable cv;
std::mutex mtx;
bool flag = false;
int counter = 0;
void producer(std::string name)
{
// 通过条件变量可以控制线程的执行顺序
std::cout << name << std::endl;
for(int i = 0; i < 10; ++i)
{
{
std::unique_lock<std::mutex> lock(mtx);
while(flag)
cv.wait(lock); // wait时会自动将锁解锁
std::cout << ++counter << std::endl;
flag = !flag;
cv.notify_one();
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void consumer(std::string name)
{
std::cout << name << std::endl;
for(int i = 0; i < 10; ++i)
{
{
std::unique_lock<std::mutex> lock(mtx);
while(!flag)
cv.wait(lock);
std::cout << ++counter << std::endl;
flag = !flag;
cv.notify_one(); //唤醒一个线程
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
int main() {
std::thread t1(producer, "thread 1");
std::thread t2(consumer, "thread 2");
t1.join();
t2.join();
return 0;
}
C++20 信号量 的工作原理。
在c++20中新增了轻量级的线程控制机制——信号量,我们可以为信号量设定最大值与初始值,当信号量达到0或者最大值时,线程会阻塞,直到其不为0或最大值。
// 头文件 <semaphore>
template< std::ptrdiff_t LeastMaxValue = /* implementation-defined */ >
class counting_semaphore;
//尚未定义ptrdiff_t 一般为int,用于定义信号量的最大值。
- 成员函数:
- release:释放信号量,将信号量计数加 1 ,如果计数达到最大值则阻塞等待,知道信号量被消费。
- acquire:申请信号量,将信号量的计数减 1 ,当计数为零或为满则阻塞等待,直到其他线程释放信号量。
- try_acquire:尝试去减少信号量的计数,如果信号量为零则返回false,并且一直申请信号量直至成功。一般用于信号量消费较快的场景。
函数使用
int counter = 0;
void producer(std::string name)
{
std::cout << name << std::endl;
for(int i = 0; i < 10; ++i)
{
sem.try_acquire(); //信号量也可以用来代替锁
std::cout << ++counter << std::endl;
sem.release();
}
}
void consumer(std::string name)
{
std::cout << name << std::endl;
for(int i = 0; i < 10; ++i)
{
sem.try_acquire();
std::cout << ++counter << std::endl;
sem.release();
}
}
int main() {
std::thread t1(producer, "thread 1");
std::thread t2(consumer, "thread 2");
t1.join();
t2.join();
return 0;
}
📓总结
功能类别 | 关键特性 | 描述 |
---|---|---|
线程管理 | std::thread | 抽象系统级线程,简化线程创建和管理。 |
基础同步 | mutex , lock_guard , unique_lock | 提供基本的线程同步机制,如互斥锁,以及自动锁管理工具。 |
高级同步 | condition_variable | 使线程能在特定条件下挂起和唤醒,优化资源利用和线程协调。 |
C++20新特性 | counting_semaphore | 引入信号量概念,为线程提供更细粒度的控制,如限流并发操作。 |