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

线程局部存储(Thread-Local Storage, TLS)

目录

一、线程局部存储(Thread-Local Storage, TLS)的演示使用代码

二、代码分析

1、线程局部存储(__thread关键字)

2、多线程创建与管理

(1) 线程创建

(2) 线程等待

3、线程函数(routine1 和 routine2)

(1) routine1:修改线程局部变量

(2) routine2:读取线程局部变量

4、地址打印函数(Addr)

5、代码执行结果分析

6、线程局部存储的局限性

7、潜在问题与改进

8、总结


一、线程局部存储(Thread-Local Storage, TLS)的演示使用代码

#include <pthread.h>
#include <iostream>
#include <string>
#include <unistd.h>// 该count叫做线程的局部存储!
__thread int count = 1;
// 线程局部存储有什么用?全局变量,我又不想让这个全局变量被其他线程看到!
// 线程局部存储,只能存储内置类型和部分指针std::string Addr(int &c)
{char addr[64];snprintf(addr, sizeof(addr), "%p", &c);return addr;
}void *routine1(void *args)
{(void)args;while (true){std::cout << "thread - 1, count = " << count << "[我来修改count], "<< "&count: " << Addr(count) << std::endl;count++;sleep(1);}
}void *routine2(void *args)
{(void)args;while (true){std::cout << "thread - 2, count = " << count<< ", &count: " << Addr(count) << std::endl;sleep(1);}
}int main()
{pthread_t tid1, tid2;pthread_create(&tid1, nullptr, routine1, nullptr);pthread_create(&tid2, nullptr, routine2, nullptr);pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);return 0;
}

二、代码分析

        这段代码演示了线程局部存储(Thread-Local Storage, TLS)的使用,以及多线程环境下变量的访问行为。下面我将详细讲解其中的知识点:

1、线程局部存储(__thread关键字)

__thread int count = 1;

作用__thread 是 GCC 提供的扩展关键字,用于声明线程局部存储变量。每个线程会拥有该变量的独立副本,线程间不会相互干扰。

特点

  • 每个线程访问的是自己的副本,修改不会影响其他线程。

  • 适用于全局变量但需要线程隔离的场景。

  • 只能用于内置类型部分指针(复杂对象如 std::string 不支持)。

对比普通全局变量

  • 普通全局变量会被所有线程共享,多线程修改会导致竞争条件(需用互斥锁保护)。

  • 线程局部存储变量无需锁,因为天然隔离。

2、多线程创建与管理

(1) 线程创建

pthread_t tid1, tid2;
pthread_create(&tid1, nullptr, routine1, nullptr);
pthread_create(&tid2, nullptr, routine2, nullptr);

函数原型

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);

参数说明

  • thread:输出参数,保存线程 ID。

  • attr:线程属性(如栈大小、调度策略),nullptr 表示默认属性。

  • start_routine:线程入口函数。

  • arg:传递给入口函数的参数(此处未使用,强制转换为 (void)args 避免编译器警告)。

(2) 线程等待

pthread_join(tid1, nullptr);
pthread_join(tid2, nullptr);

作用:主线程等待子线程结束,避免主线程退出导致子线程被强制终止。

参数

  • 第一个参数:线程 ID。

  • 第二个参数:用于获取线程返回值(此处未使用)。

3、线程函数(routine1 和 routine2

(1) routine1:修改线程局部变量

void *routine1(void *args) {while (true) {std::cout << "thread - 1, count = " << count << "[我来修改count], "<< "&count: " << Addr(count) << std::endl;count++; // 修改的是线程1的副本sleep(1);}
}

行为

  • 每秒输出当前线程的 count 值及其地址。

  • 修改 count(仅影响线程1的副本)。

(2) routine2:读取线程局部变量

void *routine2(void *args) {while (true) {std::cout << "thread - 2, count = " << count<< ", &count: " << Addr(count) << std::endl;sleep(1);}
}

行为

  • 每秒输出当前线程的 count 值及其地址。

  • 不修改 count,但即使修改也不会影响线程1的副本。

4、地址打印函数(Addr

std::string Addr(int &c) {char addr[64];snprintf(addr, sizeof(addr), "%p", &c);return addr;
}

作用:将变量的地址格式化为字符串,用于验证不同线程的 count 是否为同一内存地址。

关键点

  • 线程局部存储的变量在不同线程中地址不同(因为每个线程有独立副本)。

  • 如果是普通全局变量,所有线程打印的地址会相同。

5、代码执行结果分析

预期输出

  • 两个线程输出的 count 值初始均为 1,但后续互不影响。

  • 线程1的 count 会递增(如 1, 2, 3,...),而线程2的 count 始终为 1(除非线程2也修改自己的副本)。

  • 两个线程打印的 &count 地址不同,证明是独立副本。

示例片段

6、线程局部存储的局限性

  • 仅支持简单类型:如 intfloat、指针等,不支持 std::string 等复杂对象(会引发编译错误)。

  • 初始化时机:变量在首次访问时初始化,线程退出时销毁。

  • 替代方案:C++11 提供了更标准的 thread_local 关键字:

    thread_local int count = 1; // 功能与 __thread 类似

7、潜在问题与改进

  • 无限循环:线程函数没有退出条件,实际使用时需通过标志位控制。

  • 输出竞争std::cout 是共享资源,多线程同时输出可能导致交错(此处因 sleep(1) 延迟较难重现,但严格来说应加锁)。

  • C++11 线程库:建议使用 <thread> 替代 POSIX 线程(更现代、类型安全):

    #include <thread>
    std::thread t1(routine1, nullptr);
    std::thread t2(routine2, nullptr);
    t1.join(); t2.join();

8、总结

  • 核心知识点:线程局部存储(__thread/thread_local)实现了变量的线程隔离。

  • 关键观察:通过打印地址验证不同线程的变量副本独立性。

  • 应用场景:需要全局变量但又避免多线程竞争时(如线程独立的计数器、临时缓冲区等)。

http://www.dtcms.com/a/544462.html

相关文章:

  • 勇立潮头:优艾智合打造“一脑多态”工业具身智能新范式
  • 怕故障?怕扩展难?分布式可视化控制:给足场景安全感
  • HTML5 Audio(音频)
  • 返利网一类的网站怎么做网站设计与网页制作模板
  • CMD 的 echo 不支持像 Linux 那样用引号输出多行内容
  • 网站建设的优缺点域名换了网站需要备案么
  • 高级Web前端开发工程师2025年面试题总结及参考答案【含刷题资源库】
  • 关于 Flink 程序打包与分布式执行的详细指南
  • mysql8.4.6 LTS 主从架构搭建
  • C#实现智能提示输入,并增色显示
  • CommunityToolkit.Mvvm框架
  • 快速创建Word箱单(1/2)
  • 营销型网站建设公司易网拓做网站属于什么费用
  • 马蜂窝网络营销网站建设手机编程工具
  • iOS 抓包实战 从原理到复现、定位与真机取证全流程
  • 宝塔反向代理后就访问不到django服务中间件匹配的图片文件夹中的图片了
  • 【网络核心协议全景解析】IP、TCP、UDP与HTTP(多表格深度对比)
  • GStreamer 和 FFmpeg 两大开源工具简要对比
  • Fastlane 结合 开心上架(Appuploader)命令行实现跨平台上传发布 iOS App 的完整方案
  • Rust 中 WebSocket 支持的实现:从协议到生产级应用
  • LangChain生态介绍与实战
  • 前端基础之《React(5)—webpack简介-集成CSS和SASS支持》
  • 国外手机网站源码邵阳 做网站公司
  • 机器学习(3)---线性算法,决策树,神经网络,支持向量机
  • 网站建设服务费属于什么科目中山 灯饰 骏域网站建设专家
  • 操作系统(9)虚拟内存-内存映射
  • 30. 文件IO (1)
  • 技术深析:衡石 Agentic BI 的架构革命与核心技术突破
  • UVa 12333 Revenge of Fibonacci
  • rank(A+E) >= rank(A)证明