再见了!atomic!!

文摘   2024-12-18 12:03   新加坡  


你好,我雨乐~

今天,我们聊聊多线程编程中的一个新特性std::latch

假如有这样一个需求,并发处理多个任务,只有这几个任务都完成以后,才继续执行后面的流程。我想,很多人跟我一样,第一反应是使用std::atomic来处理,如下:

std::atomic< int > counter ( 0 ) ; //共享原子计数器

void  worker ()  {
    std::cout <<"启动工作线程"<< std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    count.fetch_add(1);// 将计数器增加 1
    std::cout <<"工作线程完成"<< std::endl;
}

int  main ()  {
std::thread t1 (worker) ;
std::thread t2 (worker) ;
std::thread t3 (worker) ;

while(count <3){
// 等待计数器达到线程数
}

    t1.join();
    t2.join();
    t3.join();

    std::cout <<"所有工作线程均已完成..."<< std::endl;

return0;
}

这段代码在实现上没问题,但是有一种更为直观的方式,那就是C++20引入的std::latch

latch

这块,我想用中文翻译来着,无奈总感觉有些别扭,所以还是保持原称比较好。

概念

std::latch 是 C++20 引入的一个同步原语,用于在多线程环境中协调多个线程的执行,确保某些线程在执行继续之前等待其他线程达到某个特定的状态。通过一个计数器来控制线程的同步。当计数器的值为 0 时,所有等待的线程可以继续执行。否则,调用 wait() 方法的线程将会阻塞,直到计数器值递减至 0。

使用

现在使用std::latch来实现文章一开始的功能,如下:

#include <iostream>
#include <thread>
#include <latch>  // 引入 std::latch 需要这个头文件

std::latch lock(3);// 创建一个 latch,计数器初始为 3

void worker() {
    std::cout <<"启动工作线程"<< std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));// 模拟工作
    std::cout <<"工作线程完成"<< std::endl;
lock.count_down();// 每个线程完成工作后,将 latch 计数器减 1
}

int main() {
std::thread t1(worker);// 创建线程 t1
std::thread t2(worker);// 创建线程 t2
std::thread t3(worker);// 创建线程 t3

lock.wait();// 主线程在这里等待,直到 latch 的计数器为 0,即所有线程都调用了 count_down()

    t1.join();// 等待 t1 完成
    t2.join();// 等待 t2 完成
    t3.join();// 等待 t3 完成

    std::cout <<"所有工作线程均已完成..."<< std::endl;

return0;
}

在上面代码中:

创建一个全局对象std::latch对象lock,并将其初始值设置为3。这意味着主线程将会等待,直到有 3 个线程调用 lock.count_down()worker 函数模拟了一个工作线程。每个工作线程在启动时会打印 "启动工作线程",然后休眠 1 秒钟(模拟一些任务),最后打印 "工作线程完成",并调用 lock.count_down(),使得 lock 的计数器减少 1主线程调用 lock.wait(),它将阻塞等待,直到 lock 的计数器变为 0当每个工作线程调用 lock.count_down() 后,计数器将逐渐减少。因为初始值为 3,所以每个线程完成后将计数器减少 1。等到所有 3 个线程都调用了 count_down(),计数器变为 0,主线程的 wait() 调用将返回,继续执行主线程中的代码。主线程在 wait() 返回后,调用 t1.join()t2.join(), 和 t3.join(),等待所有线程完成。最后,主线程打印 "所有工作线程均已完成..."

需要注意的是 lock.wait()返回的前提是 lock的计数器为0,否则一直等待~

以上

如果对本文有疑问可以加笔者微信直接交流,笔者也建了C/C++相关的技术群,有兴趣的可以联系笔者加群。


推荐阅读  点击标题可跳转

1、呃,竟然死锁了~

2、工作三年,我才搞懂了这俩关键字

3、几种常见特性的编译器实现



雨乐聊编程
毕业于中国科学技术大学,现任某互联网公司高级技术专家一职。本公众号专注于技术交流,分享日常有意思的技术点、线上问题等,欢迎关注