当前位置: 首页 > news >正文

C++ Latch 和 Barrier: 新手指南

文章目录

    • 什么是 Latch 和 Barrier?
    • 为什么要使用 Latch 和 Barrier?
    • 代码示例
      • 示例 1: 使用 `std::latch`
      • 示例 2: 多阶段任务
      • 示例 3: 使用 `std::barrier`
    • 何时使用?
    • 优势
    • 使用时需要注意的事项
    • 参考链接
    • 源码链接

随着并发和并行编程的重要性日益增加, 理解像 LatchBarrier 这样的同步原语对于现代 C++ 开发者来说至关重要. 这些工具在 C++20 中引入, 用于高效地协调多线程工作. 本文将概述它们的用法, 好处以及实际示例.


什么是 Latch 和 Barrier?

  1. Latch(门闩):

    • std::latch 是一种一次性使用的同步原语, 允许一个线程(或一组线程)等待, 直到计数器减少到零.
    • 使用场景: 通常用于确保一组线程在满足某个前置条件(例如初始化资源)之前不会继续执行.
  2. Barrier(屏障):

    • std::barrier 是一种可重复使用的同步原语, 允许一组线程反复等待, 直到所有线程到达某个点(称为阶段或屏障点).
    • 使用场景: 适用于将工作划分为多个阶段的算法, 确保所有线程完成一个阶段后再进入下一个阶段.

为什么要使用 Latch 和 Barrier?

  • 提高代码可读性: 与使用互斥锁或条件变量的手动实现相比, 同步逻辑更易读, 更易维护.
  • 性能优化: 这些原语针对特定使用场景进行了优化, 比通用同步工具性能更好.
  • 避免死锁: 通过明确定义同步点, 减少代码中引入死锁的可能性.

代码示例

示例 1: 使用 std::latch

在这个示例中, 我们有多个线程模拟不同的任务工作. 主线程需要等待所有工作线程完成任务后, 才继续后续的操作.

#include <iostream>
#include <latch>
#include <random>
#include <syncstream>
#include <thread>
#include <vector>void worker(std::latch& latch, int id) {// 使用 osyncstream 同步输出{std::osyncstream sync_out(std::cout);sync_out << "线程 " << id << " 正在工作...\n";}// 生成一个随机值, 范围为[100, 500]std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(100, 500);int sleep_duration = dis(gen);// sleep一段时间, 模拟工作std::this_thread::sleep_for(std::chrono::milliseconds(sleep_duration));// 发布完成信号并同步输出{std::osyncstream sync_out(std::cout);sync_out << "线程 " << id << " 完成了任务. \n";}latch.count_down();
}int main() {constexpr int num_threads = 5;std::latch latch(num_threads);std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back(worker, std::ref(latch), i + 1);}// 等待所有线程完成任务latch.wait();std::cout << "所有线程已完成任务. \n";for (auto& t : threads) {t.join();}return 0;
}

输出:

线程 1 正在工作...
线程 5 正在工作...
线程 3 正在工作...
线程 4 正在工作...
线程 2 正在工作...
线程 4 完成了任务.
线程 2 完成了任务.
线程 3 完成了任务.
线程 5 完成了任务.
线程 1 完成了任务.
所有线程已完成任务.

输出解释:

  • 各线程独立工作, 并在完成后递减 latch 计数器.
  • main 线程等待计数器归零后再继续执行.

示例 2: 多阶段任务

下面这个例子演示了多阶段任务中不同线程之间的同步. 此时latch的局限性就显示出来了: 需要声明多个latch, 这不是好的写法. 下个例子中将展示如何用barrier更好的解决这个问题.

#include <iostream>
#include <latch>
#include <syncstream>
#include <thread>
#include <vector>void worker(std::latch& latchA, std::latch& latchB, std::latch& latchC,int id) {// 任务Astd::this_thread::sleep_for(std::chrono::milliseconds(100 + id * 100));{std::osyncstream sync_out(std::cout);sync_out << "线程 " << id << " 完成了任务 A. \n";}latchA.arrive_and_wait();// 任务Bstd::this_thread::sleep_for(std::chrono::milliseconds(100 + id * 100));{std::osyncstream sync_out(std::cout);sync_out << "线程 " << id << " 完成了任务 B. \n";}latchB.arrive_and_wait();// 任务Cstd::this_thread::sleep_for(std::chrono::milliseconds(100 + id * 100));{std::osyncstream sync_out(std::cout);sync_out << "线程 " << id << " 完成了任务 C. \n";}latchC.arrive_and_wait();
}int main() {constexpr int num_threads = 5;std::latch latchA(num_threads);std::latch latchB(num_threads);std::latch latchC(num_threads);std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back(worker, std::ref(latchA), std::ref(latchB),std::ref(latchC), i + 1);}for (auto& t : threads) {t.join();}return 0;
}

输出:

线程 1 完成了任务 A.
线程 2 完成了任务 A.
线程 3 完成了任务 A.
线程 4 完成了任务 A.
线程 5 完成了任务 A.
线程 1 完成了任务 B.
线程 2 完成了任务 B.
线程 3 完成了任务 B.
线程 4 完成了任务 B.
线程 5 完成了任务 B.
线程 1 完成了任务 C.
线程 2 完成了任务 C.
线程 3 完成了任务 C.
线程 4 完成了任务 C.
线程 5 完成了任务 C.

可以看到任务 A,B,C 是依次被完成的. 没有出现任务之间的乱序.

示例 3: 使用 std::barrier

在这个示例中, 我们设计了一个分阶段的任务, 每个线程在每个阶段完成工作后需要等待其他线程同步, 然后再进入下一阶段.

#include <barrier>
#include <chrono>
#include <iostream>
#include <syncstream>
#include <thread>
#include <vector>void phase_work(std::barrier<>& barrier, int id) {for (char phase = 'A'; phase < 'D'; ++phase) {// 模拟当前阶段的工作std::this_thread::sleep_for(std::chrono::milliseconds(100 * id));// 确保当前线程的输出不被其他线程干扰std::osyncstream sync_out(std::cout);// 等待所有线程完成当前阶段sync_out << "线程 " << id << " 完成了任务 " << phase << ". \n";barrier.arrive_and_wait();}
}int main() {constexpr int num_threads = 5;std::barrier barrier(num_threads);std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&barrier, i]() { phase_work(barrier, i + 1); });}for (auto& t : threads) {t.join();}return 0;
}

输出:

线程 1 完成了任务 A.
线程 2 完成了任务 A.
线程 3 完成了任务 A.
线程 4 完成了任务 A.
线程 5 完成了任务 A.
线程 1 完成了任务 B.
线程 2 完成了任务 B.
线程 3 完成了任务 B.
线程 4 完成了任务 B.
线程 5 完成了任务 B.
线程 1 完成了任务 C.
线程 2 完成了任务 C.
线程 3 完成了任务 C.
线程 4 完成了任务 C.
线程 5 完成了任务 C.

输出解释:

  • 每个线程处理一个阶段, 并在屏障点等待.
  • 可选的完成操作在所有线程完成阶段后执行.

何时使用?

  • Latch:

    • 当需要一次性的同步点时.
    • 示例: 确保所有资源初始化完成后再开始处理.
  • Barrier:

    • 当需要多次迭代任务并在每次迭代后同步时.
    • 示例: 具有阶段执行的并行算法, 如矩阵乘法.

优势

  • 易用性: 相比传统同步机制, 减少了样板代码.
  • 可扩展性: 为高性能多线程程序设计.
  • 调试友好: 简化线程协调的逻辑, 降低错误概率.

使用时需要注意的事项

  1. Latch 的常见问题:

    • 计数器不足或过多: 确保 count_down 调用的次数准确. 如果某些线程未正确调用 count_down, 可能导致 wait 永远不会返回.
      • 解决方法: 在代码逻辑中严格控制 count_down 的调用次数.
    • 无法重复使用: std::latch 是一次性的, 如果需要多次使用, 请选择 std::barrier.
  2. Barrier 的常见问题:

    • 线程不平衡: 某些线程可能比其他线程运行得更慢, 导致其他线程在 arrive_and_wait 阻塞过久.
      • 解决方法: 优化线程的工作量, 尽量均衡任务分配.
    • 未正确完成一个阶段: 如果某个线程在某一阶段抛出异常或终止, 可能会导致整个程序卡在屏障点.
      • 解决方法: 确保所有线程在异常情况下也能够安全退出, 或捕获异常并手动调用屏障完成操作.
  3. 资源释放问题:

    • 如果在 latchbarrier 的作用范围内提前释放相关资源, 可能会导致未定义行为.
      • 解决方法: 确保 latchbarrier 的生命周期覆盖所有线程的操作.
  4. 死锁风险:

    • 如果线程逻辑中存在相互依赖关系, 可能会导致死锁.
      • 解决方法: 尽量减少线程之间的依赖, 并确保每个线程都能独立完成其任务.

通过使用 std::latchstd::barrier, C++ 开发者可以编写出更健壮, 可读性更高, 性能更优的多线程程序. 无论您是并发编程的新手还是经验丰富的开发者, 这些工具都应该成为您的编程工具箱的一部分!

参考链接

  • std::latch - cppreference.com
  • std::barrier - cppreference.com
  • C++20 屏障 std::latch 哔哩哔哩
  • C++20 屏障 std::barrier 哔哩哔哩

源码链接

源码链接

相关文章:

C++ Latch 和 Barrier: 新手指南

文章目录 什么是 Latch 和 Barrier?为什么要使用 Latch 和 Barrier?代码示例示例 1: 使用 std::latch示例 2: 多阶段任务示例 3: 使用 std::barrier 何时使用?优势使用时需要注意的事项参考链接源码链接 随着并发和并行编程的重要性日益增加, 理解像 Latch 和 Barrier 这样的…...

【Cocos TypeScript 零基础 4.1】

目录 背景滚动 背景滚动 创建一个 空节点 背景丟进去 ( 复制一个,再丢一次都行) 新建TS脚本 并绑定到 空节点 上 再对TS脚本进行编辑 export class TS2bg extends Component {property (Node) // 通过属性面板去赋值bg1:Node nullproperty (Node) bg2:Node nullprope…...

区块链安全常见的攻击合约和简单复现,附带详细分析——不安全调用漏洞 (Unsafe Call Vulnerability)【6】

区块链安全常见的攻击分析——不安全调用漏洞 Unsafe Call Vulnerability 1.1 漏洞合约1.2 漏洞分析1.3 攻击步骤分析1.4 攻击合约 Name: 不安全调用漏洞 (Unsafe Call Vulnerability) 重点&#xff1a; 在 TokenWhale 合约的 approveAndCallcode 函数中&#xff0c;漏洞允许任…...

鸿蒙应用开发搬砖经验之—使用ArkWeb要开启文档对象模型存储接口权限(DOM Storage API权限)

如题&#xff0c;该属性/功能默认是没有开启的&#xff01;&#xff01;&#xff01;&#xff01; 所以需要我们手动开启&#xff0c;否侧加载的H5 SPA大概率功能不正常&#xff0c;因为现在大多数的H5应用都用遇到对象模型存储的功能&#xff0c;对应的接口是 不开启一般会…...

本机实现Llama 7B推理及部署

本机实现Llama 7B推理及部署 使用llamafile在Windows系统部署 部署步骤:首先从https://www.modelscope.cn/api/v1/models/bingal/llamafile-models/repo?Revision=master&FilePath=llamafile-0.6.2.win.zip下载llamafile并解压得到llamafile.exe文件, 再从https://www.…...

Spring Boot 依赖配置分离多种打包方式

生产上发布 Spring Boot 项目时,但凡代码有一丁点改动,就得把整个项目包括依赖重新打包上传部署,这样的包很大,影响效率 为解决这个问题,可以把依赖(pom中的依赖jar包)、配置文件(resources 下的 applacation.yml 等文件)从项目主体里剥离出来,后续部署时,只需发布代…...

华为的数字化转型框架和数字化转型成熟度评估方法

2016年&#xff0c;华为公司数字化转型变革规划汇报通过&#xff0c;一系列的变革项目由变革指导委员会(Executive Steering Committee,ESC)完成立项。8年多来&#xff0c;华为数字化转型工作初步取得了一些成果&#xff0c;比如&#xff1a; 实现“销售收入翻番&#xff0c;但…...

图像转换 VM与其他格式互转

目录 前言 图像转换 1.相机取流转VM对应类型图像格式 1.1 相机采图转流程输入和Group输入(ImageBaseData_V2) 1.2 相机采图转图像源SDK输入(ImageBaseData) 1.3 相机采图转模块输入(InputImageData) 1.4 相机采图转算子输入(CmvdImage) 2.Bitmap取图与VM对应图像格式互…...

气象白化的三种方法

【总结】cnmaps、maskout、salem的正确打开方式 - 知乎https://zhuanlan.zhihu.com/p/636252854总结了三种方式&#xff0c;比较还是安装了Salem库&#xff0c;第一次import联网下载也很顺利&#xff01;&#xff01;&#xff01;...

Azkaban3.84集群安装部署

基础环境配置 上传安装包并解压 tar -zxvf azkaban-exec-server-3.84.4.tar.gz -C /ddhome/bin/ tar -zxvf azkaban-web-server-3.84.4.tar.gz -C /ddhome/bin/ tar -zxvf azkaban-db-3.84.4.tar.gz -C /ddhome/bin/mv azkaban-exec-server-3.84.4 azkaban-exec mv azkaban-w…...

XIAO Esp32S3制作网络摄像头——1音频获取

1、功能介绍 本文主要是基于XIAO Esp32S3(Sense)做的一款网络摄像头,主要包含以下功能 1 音频获取/保存 2 视频获取/视频保存 3 行人检测/火焰检测/行人追踪(告警) 4 指定区域 5 摄像头旋转 。。。 本文主要实现第一步,音频获取,后续会陆续实现后面的功能,敬请期…...

【Axios使用手册】如何使用axios向后端发送请求并进行数据交互

axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于浏览器和 Node.js。它支持请求和响应拦截、取消请求、自动转换 JSON 数据等功能&#xff0c;非常适合在现代 JavaScript 应用中进行网络请求。以下是对 axios 的详细讲解&#xff0c;包括安装、基本用法、高级功能等。…...

groupby 操作的不同参数

groupby 是数据分析中一个非常强大的操作&#xff0c;可以根据指定的规则将数据拆分成多个组&#xff0c;并对每个组进行聚合、转换或过滤等操作。我们逐个解释这些参数的作用&#xff0c;并通过数值举例进行说明。 参数解释 by&#xff1a;分组依据 by 参数指定了分组的依据&…...

组合模式——C++实现

1. 模式简介 组合模式是一种结构型模式。 组合模式又叫做部分整体模式&#xff0c;组合模式用于把一组相似的对象当做一个单一的对象。特别擅长处理树形的数据&#xff0c;对于非树形的数据不好用它。 对于树形的数据&#xff0c;一个典型的例子就是文件系统。在文件系统里大致…...

【开源监控工具】Uptime Kuma:几分钟设置实时监控你的网站性能

文章目录 前言1.关于Uptime Kuma2.安装Docker3.本地部署Uptime Kuma4.使用Uptime Kuma5.cpolar内网穿透工具安装6.创建远程连接公网地址7.固定Uptime Kuma公网地址 前言 大家好&#xff01;如果你是网站运维人员或者管理着多个站点&#xff0c;那么今天我要介绍的一款工具绝对…...

MATLAB画柱状图

一、代码 clear; clc; figure(position,[150,100,900,550])%确定图片的位置和大小&#xff0c;[x y width height] %准备数据 Y1[0.53,7.9,8.3;0.52,6.8,9.2;0.52,5.9,8.6;2.8,5.8,7.9;3.9,5.2,7.8;1.8,5.8,8.4]; % withoutNHC X11:6; %画出4组柱状图&#xff0c;宽度1 h1…...

stm32内部flash在线读写操作

stm32内部flash在线读写操作 &#x1f4cd;相关开源库文章介绍《STM32 利用FlashDB库实现在线扇区数据管理不丢失》 ✨不同系列&#xff0c;内部flash编程有所区别。例如stm32f1是按照页擦除&#xff0c;半字&#xff08;16bit&#xff09;或全字(32bit)数据写入&#xff1b;st…...

SpringCloud源码分析-nacos与eureka

一、高版本为什么优先用nacos 如果用alibaba springcloud&#xff0c;那么就是阿里的技术体系。nacos属于阿里的原生技术栈&#xff0c;所以阿里更偏向于用nacos作为服务发现注册。 二、对比分析 Spring Cloud Alibaba 推荐使用 Nacos 作为服务发现和配置管理的首选组件&…...

DCGAN模型详解

模型背景 在深度学习领域迅速发展的背景下,生成对抗网络(GAN)作为一种革命性的生成模型应运而生。 Ian Goodfellow等人于2014年首次提出GAN概念 ,开创了生成模型的新纪元。这一创新源于对深度学习在图像生成方面潜力的探索,旨在解决非监督学习中的关键问题:如何让机器创造…...

单片机-蜂鸣器实验

#include "reg52.h" typedef unsigned char u8; typedef unsigned int u16; sbit BEEPP2^5; //将 P2.5 管脚定义为 BEEP P2.5默认高电平 void delay_10us(u16 ten_us){ while(ten_us--); } void main() { u16 i2000;//脉冲2000次 while(1) { …...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...