C++ 基于 TLS(thread local storage)的 TID 获取

前言:

C++ 中,通过 thread_local 关键字来声明一个线程局部存储变量;同时,可以通过原子操作来保证变量的加载和存储操作;

现在,我们将通过 TLS 和 原子操作,实现高频率的 tid 获取程序;在此之前,说明一下使用 gettid() 的劣势,gettid() 涉及到系统调用,在高频系统中,会增加额外的开销(内核和系统之间来回切换);

步骤 1: 引入必要的库

我们需要 atomicthread_local 来实现线程局部存储和原子操作:

#include <atomic>
#include <thread>
#include <unistd.h>  // 用于 syscalls,例如 gettid()
步骤 2: 使用 thread_localstd::atomic 实现线程局部存储

在 C++ 中,我们可以将 cached_vtid 声明为线程局部存储的变量,并使用原子操作来进行加载和存储。

// 声明线程局部存储变量
thread_local std::atomic<pid_t> cached_vtid{0};// 用于清除缓存的函数,类似于 lttng_context_vtid_reset
void reset_cached_vtid() {cached_vtid.store(0, std::memory_order_release);  // 清空缓存
}
步骤 3: 实现获取 TID 的函数
inline pid_t get_vtid() {pid_t vtid = cached_vtid.load(std::memory_order_acquire);  // 获取缓存的 TIDif (vtid == 0) {  // 如果缓存为空vtid = static_cast<pid_t>(syscall(SYS_gettid));  // 调用系统调用获取 TIDcached_vtid.store(vtid, std::memory_order_release);  // 缓存新的 TID}return vtid;  // 返回缓存或系统调用获取的 TID
}
步骤 4: 记录 TID

如果需要将 TID 存储到某个结构中,可以像以下方式进行操作:

void record_vtid(void* ctx, size_t offset) {pid_t vtid = get_vtid();  // 获取 TID// 假设我们有一个写入的操作// 这里可以替换为相应的 ring buffer 写入操作,假设是写入到 `chan` 中// 这里的 `ctx` 和 `offset` 是为了与原始代码保持一致// event_write(ctx, &vtid, sizeof(vtid), offset);  // 假设写入操作
}

代码总结

#include <atomic>
#include <thread>
#include <unistd.h>  // gettid// 线程局部存储,用于缓存 TID
thread_local std::atomic<pid_t> cached_vtid{0};// 用于清空缓存
void reset_cached_vtid() {cached_vtid.store(0, std::memory_order_release);
}// 获取线程 TID,采用原子操作避免系统调用
inline pid_t get_vtid() {pid_t vtid = cached_vtid.load(std::memory_order_acquire);  // 获取缓存的 TIDif (vtid == 0) {  // 如果缓存为空vtid = static_cast<pid_t>(syscall(SYS_gettid));  // 调用系统调用获取 TIDcached_vtid.store(vtid, std::memory_order_release);  // 缓存新的 TID}return vtid;  // 返回缓存或系统调用获取的 TID
}// 记录 TID
void record_vtid(void* ctx, size_t offset) {pid_t vtid = get_vtid();  // 获取 TID// 假设我们将 TID 写入某个缓冲区// event_write(ctx, &vtid, sizeof(vtid), offset);  // 假设写入操作
}

解释

  1. thread_local:确保每个线程有自己的 cached_vtid 变量,这样就可以避免线程间共享数据,确保线程安全。
  2. std::atomic<pid_t>std::atomic 确保对 cached_vtid 的读写是原子的,不会出现竞态条件。
  3. syscall(SYS_gettid):调用 gettid() 系统调用获取线程的 TID。由于我们在没有缓存时才调用系统调用,因此可以减少不必要的系统调用。

额外的注意点

  • 如果程序有 forkclone 操作,可能需要清除缓存,因为这些操作会导致 TID 改变(特别是在子进程中)。reset_cached_vtid 可以在这些操作后调用,确保缓存被清空。
  • 如果你的程序中使用了 liburcu 或其他线程同步机制,可能还需要结合特定的内存模型或同步原语来确保线程间的数据一致性。

这样,你就成功地将 C 中基于 URCU_TLS 的线程局部存储缓存机制迁移到了 C++ 中,利用了 thread_localstd::atomic 实现了线程安全的 TID 缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/16407.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue3【组件封装】S-icon 图标 ( 集成 iconify )

1. 安装依赖 npm i -D iconify/jsonnpm i --save-dev iconify/vue2. 组件封装 src/components/S-icon.vue <script setup lang"ts"> // 搜索图标 https://icon-sets.iconify.design/ import { Icon } from iconify/vue defineProps({icon: {type: String,requ…

AI视觉小车基础--1.开发前的准备

1. 结束开机大程序 为了能够方便体验小车的APP功能&#xff0c;系统中增加了一个程序&#xff0c;此程序集合了APP的控制功能和玩法&#xff0c;所以称做“大程序”&#xff0c;而且在主板系统开机时&#xff0c;此程序会自动启动&#xff0c;所以称做“开机自启动大程序”。 开…

6.传输层协议、ACL

TCP和UDP协议 TCP/IP协议组的传输层协议 TCP(Transmission Control Protocol ) 传输控制协议 UDP&#xff08;User Datagram Protocol &#xff09; 用户数据报协议 TCP协议 TCP是面向连接的、可靠的进程到进程通信的协议 TCP提供全双工服务&#xff0c;即数据可在同一时间双…

避免数据丢失!在NAS上保存Docker容器配置的正确姿势

引言 如果你使用NAS来管理家庭或小型企业的数据,那么Docker容器一定不陌生。它能快速部署各种应用,比如Jellyfin、Plex等多媒体服务器。然而,很多人却踩过一个坑:NAS关机重启后,Docker容器的配置居然丢了!辛苦搭建的环境瞬间化为乌有。别担心,今天就来分享一套实用的技…

【Java的动态代理】

Java中有两种实现动态代理的方式jdk动态代理和CGLIB动态代理 jdk动态代理: 基于接口的动态代理, 目标对象必须实现接口cglib动态代理: 基于字节生成技术(ASM代码生成库), 能在运行时对java类和接口进行扩展实现 那么动态代理这个技术到底能帮我们干啥? 个人感觉这个技术在框…

python怎么安装numpy

1、在python官网https://pypi.python.org/pypi/numpy中找到安装的python版本对应的numpy版本。 例如&#xff1a; python版本是&#xff1a; 下载的对应numpy版本是&#xff1a; 2、将numpy下载到python的安装目录下的scripts文件夹中&#xff1b; 3、然后在cmd中执行以下命…

计算机三级 数据库技术

第一章 数据库应用系统开发方法 1.1 数据库应用系统生命周期 软件工程:软件工程的思想&#xff0c;即用工程的概念、原理、技术和方法对软件生产、开发的全过程进行跟踪和管理 软件开发方法:瀑布模型、快速原型模型、螺旋模型 DBAS生命周期模型 1.2 规划与分析 系统规划与定…

网络编程套接字2

之前我们已经介绍了UDP套接字流程&#xff0c;接下来我们介绍TCP流套接字编程&#xff0c;TCP的一个核心特点&#xff0c;面向字节流&#xff0c;读写数据的基本单位就是字节。 1.API介绍 1.1ServerSocket:是创建TCP服务器Socket的API&#xff08;专门给服务器用&#xff09;…

偌依-防重复提交

其中的使用工具类可去偌依的代码中查找 需要配合 springboot自定义过滤器构建可重复读取inputStream的request&#xff08;来源若依&#xff09; springboot自定义过滤器构建可重复读取inputStream的request&#xff08;来源若依&#xff09;-CSDN博客 定义注解 package co…

3D 数组插值 MATLAB

插值是一种根据现有数据点创建的趋势查找查询数据点值的方法。MATLAB 提供了许多选项来对 N 维数据执行插值。 在本文中&#xff0c;我们将讨论如何借助一些示例在 3D 数组中插入数据。我们将使用 MATLAB 的 interpn&#xff08;&#xff09; 函数来执行插值。 语法 vq interp…

如何在Typora中绘制流程图

如何在Typora中绘制流程图 在撰写文档时&#xff0c;清晰的流程图能极大地提升信息传递的效率。Typora是一款优秀的Markdown编辑器&#xff0c;支持通过Mermaid语法快速绘制流程图。本文将介绍如何在Typora中创建和自定义流程图&#xff0c;帮助你用更直观的方式呈现逻辑结构和…

SpringBoot集成Redis(全流程详解)

前言 通过在SpringBoot中集成Redis&#xff0c;详细梳理集成过程。包括SpringBoot启动过程中&#xff0c;容器的刷新、自动配置的流程、各类注解的处理。 类比在纯Spring中集成Redis&#xff0c;体验SpringBoot自动配置给开发带来了哪些便利。 一、测试样例 1.1配置文件 a…

机器人控制技术、传感器技术、Wi-Fi无线通信技术、AI视觉应用教学和实训: 智能小车车臂教学平台

1、基本介绍 智能车臂教学平台在硬件上采用模块化设计&#xff0c;主控板、运动车体、机械臂、各类传感器等都可以进行拆卸操作&#xff1b;在接口上&#xff0c;采用标准拔插式设计&#xff0c;减少接线&#xff0c;方便组装。使用Wi-Fi与控制软件进行通信&#xff0c;支持遥…

ssm113ssm框架的购物网站+vue(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;网上超市系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本网上超市系统就是在这…

C++ 里面散发的咖喱味儿 - Currying函数式编程

C 里面散发的咖喱味儿 - Currying函数式编程 大家好&#xff0c;最近几篇都在聊C里面的函数式编程&#xff0c;今天我们继续就某一个点来深入聊一下&#xff0c;来聊聊在 C 中如何使用 std::bind 来实现函数式编程&#xff0c;尤其是柯里化&#xff08;Currying&#xff09;这…

【Gitee版】一篇教你如何快速入门git(详解)

前言--区分Git与Gitee Git 是一个强大的分布式版本控制系统&#xff0c;用于管理源代码。市面上有很多基于git的仓库网站&#xff0c;例如&#xff1a;GitHub、Gitee、GitCode等&#xff0c;它们之间的关系就好像是&#xff1a;git为基类&#xff0c;剩余为子类的样子。使用的…

Linux系统编程学习 NO.11——进程的概念(2)

谈谈进程的性质 进程的竞争性 由于CPU资源是稀缺的,进程数量是众多的。不可避免需要造成进程排队等待CPU资源的动作&#xff0c;内核的设计者为了让操作系统合理的去调度这这些进程&#xff0c;就产生了进程优先级的概念。设置合理的进程优先级能让不同进程公平的去竞争CPU资…

灵神 刷题DAY1

Python与java的刷题的区别 1. Python没有分号 2. Python不能return的时候赋值 3. Python没有小括号和花括号 4. Python的循环很奇怪&#xff0c;没有for(int i0;i<32;i)这种形式 而是直接用的是for i in range(n)这种 5. Python中没有 6. Python中没有&& 是an…

Nginx中使用keepalive实现保持上游长连接实现提高吞吐量示例与测试

场景 HTTP1 .1之后协议支持持久连接&#xff0c;也就是长连接&#xff0c;优点在于在一个TCP连接上可以传送多个HTTP请求和响应&#xff0c; 减少了建立和关闭连接的消耗和延迟。 如果我们使用了nginx去作为反向代理或者负载均衡&#xff0c;从客户端过来的长连接请求就会被…