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

线程特定存储

线程特定存储(Thread-Specific Storage)详解

文章目录

  • 线程特定存储(Thread-Specific Storage)详解
    • 一个 简单模拟线程局部存储的例子
    • 为什么这是线程特定存储的体现
      • 1. ​**全局访问点,线程私有数据**​
      • 2. ​**基于线程ID的数据隔离**​
      • 3. ​**线程本地上下文**​
      • 4. ​**数据生命周期管理**​
      • 5. ​**集中管理,分散存储**​
    • 线程特定存储的标准实现
    • 线程特定存储的典型应用场景
      • 1. 线程本地随机数生成器
      • 2. 数据库连接池(每个线程独立连接)
      • 3. 请求上下文(Web服务器)
    • 线程特定存储的优势
    • 注意事项
    • 总结

一个 简单模拟线程局部存储的例子

#include <iostream>
#include <thread>
#include <vector>
#include <map>
#include <mutex>
#include <random>// 线程特定的数据存储
class ThreadSpecificStorage {std::mutex storage_mutex;std::map<std::thread::id, int> storage;public:// 设置当前线程的数据void set_data(int value) {std::lock_guard<std::mutex> lock(storage_mutex);storage[std::this_thread::get_id()] = value;}// 获取当前线程的数据int get_data() {std::lock_guard<std::mutex> lock(storage_mutex);auto id = std::this_thread::get_id();return storage.count(id) ? storage[id] : -1;}// 打印所有线程的数据void print_all() {std::lock_guard<std::mutex> lock(storage_mutex);std::cout << "===== 线程数据存储 =====\n";for (const auto& [id, value] : storage) {std::cout << "线程 " << id << ": " << value << "\n";}}
};// 全局存储实例
ThreadSpecificStorage tss;void worker_task(int id) {// 设置线程特定数据tss.set_data(id * 10);// 模拟工作std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(100, 500);std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen)));// 获取并打印线程特定数据std::cout << "工作线程 " << id << " 的数据: " << tss.get_data() << "\n";
}int main() {std::vector<std::thread> workers;int num_workers = 5;// 创建工作线程for (int i = 0; i < num_workers; ++i) {workers.emplace_back(worker_task, i);}// 主线程设置自己的数据tss.set_data(999);// 等待所有线程完成for (auto& t : workers) {t.join();}// 打印所有线程的数据tss.print_all();return 0;
}

这个示例完美体现了线程特定存储(Thread-Specific Storage, TSS)​的概念,因为它实现了以下关键特性:

为什么这是线程特定存储的体现

1. ​全局访问点,线程私有数据

// 全局存储实例
ThreadSpecificStorage tss;
  • 所有线程都访问同一个全局对象tss

  • 但每个线程在tss中存储的数据是私有的,只属于该线程

  • 这就像公司有一个共享的储物柜系统,但每个员工有自己的专属格子

2. ​基于线程ID的数据隔离

void set_data(int value) {std::lock_guard<std::mutex> lock(storage_mutex);storage[std::this_thread::get_id()] = value;
}
  • 使用std::this_thread::get_id()作为键值

  • 每个线程的数据通过其唯一ID隔离

  • 线程A无法访问线程B的数据,反之亦然

3. ​线程本地上下文

void worker_task(int id) {// 设置线程特定数据tss.set_data(id * 10);// ...执行工作...// 获取线程特定数据std::cout << "工作线程 " << id << " 的数据: " << tss.get_data() << "\n";
}
  • 每个工作线程设置自己的数据(id * 10

  • 在工作过程中,随时可以访问自己的数据

  • 这类似于每个员工有自己的工作台和工具

4. ​数据生命周期管理

// 主线程设置自己的数据
tss.set_data(999);
  • 主线程和工作线程都有自己的数据存储

  • 数据生命周期与线程执行周期相关联

  • 线程结束后,其数据仍保留在存储中(直到被覆盖或清除)

5. ​集中管理,分散存储

void print_all() {std::lock_guard<std::mutex> lock(storage_mutex);std::cout << "===== 线程数据存储 =====\n";for (const auto& [id, value] : storage) {std::cout << "线程 " << id << ": " << value << "\n";}
}
  • 集中管理所有线程的数据

  • 但每个线程的数据是独立存储的

  • 管理员(主线程)可以查看所有数据,但线程只能访问自己的

线程特定存储的标准实现

C++11引入了更高效的thread_local关键字,这是标准化的线程特定存储:

#include <iostream>
#include <thread>
#include <vector>// 使用thread_local实现线程特定存储
thread_local int thread_specific_data = 0;void worker_task(int id) {// 设置线程特定数据thread_specific_data = id * 100;// 模拟工作std::this_thread::sleep_for(std::chrono::milliseconds(100));// 获取线程特定数据std::cout << "线程 " << id << " 的数据: " << thread_specific_data << "\n";
}int main() {std::vector<std::thread> workers;// 主线程设置自己的数据thread_specific_data = 9999;std::cout << "主线程初始数据: " << thread_specific_data << "\n";// 创建工作线程for (int i = 0; i < 5; ++i) {workers.emplace_back(worker_task, i);}// 主线程数据保持不变std::cout << "主线程数据未变: " << thread_specific_data << "\n";for (auto& t : workers) {t.join();}return 0;
}

线程特定存储的典型应用场景

1. 线程本地随机数生成器

#include <random>
#include <iostream>
#include <thread>
#include <vector>// 线程特定的随机数引擎
thread_local std::mt19937 rng_engine(std::random_device{}());void worker_task(int id) {// 使用线程特定的随机数引擎std::uniform_int_distribution<int> dist(1, 100);int random_value = dist(rng_engine);std::cout << "线程 " << id << " 的随机数: " << random_value << "\n";
}int main() {std::vector<std::thread> workers;for (int i = 0; i < 5; ++i) {workers.emplace_back(worker_task, i);}for (auto& t : workers) {t.join();}return 0;
}

2. 数据库连接池(每个线程独立连接)

#include <iostream>
#include <thread>
#include <vector>
#include <memory>class DatabaseConnection {
public:DatabaseConnection(int id) : connection_id(id) {std::cout << "创建数据库连接 " << connection_id << "\n";}void query(const std::string& sql) {std::cout << "连接 " << connection_id << " 执行: " << sql << "\n";}~DatabaseConnection() {std::cout << "关闭数据库连接 " << connection_id << "\n";}private:int connection_id;
};// 线程特定的数据库连接
thread_local std::unique_ptr<DatabaseConnection> db_conn;void worker_task(int id) {// 初始化线程特定的数据库连接if (!db_conn) {db_conn = std::make_unique<DatabaseConnection>(id);}// 使用连接执行查询db_conn->query("SELECT * FROM users");db_conn->query("UPDATE accounts SET balance = balance + 100");// 连接在线程结束时自动关闭
}int main() {std::vector<std::thread> workers;for (int i = 0; i < 3; ++i) {workers.emplace_back(worker_task, i);}for (auto& t : workers) {t.join();}return 0;
}

3. 请求上下文(Web服务器)

#include <iostream>
#include <thread>
#include <string>
#include <chrono>// 请求上下文(线程特定)
thread_local std::string request_id;
thread_local std::chrono::steady_clock::time_point request_start_time;void process_request(const std::string& id) {// 设置请求上下文request_id = id;request_start_time = std::chrono::steady_clock::now();// 模拟请求处理std::this_thread::sleep_for(std::chrono::milliseconds(100));// 记录请求处理时间auto duration = std::chrono::steady_clock::now() - request_start_time;auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();std::cout << "请求 " << request_id << " 处理时间: " << ms << "ms\n";
}void handle_request(const std::string& id) {try {process_request(id);} catch (...) {std::cerr << "请求 " << request_id << " 处理失败\n";}
}int main() {std::thread t1(handle_request, "req-001");std::thread t2(handle_request, "req-002");t1.join();t2.join();return 0;
}

线程特定存储的优势

  1. 避免锁竞争

    • 每个线程访问自己的数据,无需同步

    • 相比全局锁保护的共享数据,性能更高

  2. 简化API设计

    • 函数无需传递上下文参数

    • 代码更简洁,逻辑更清晰

  3. 自然隔离

    • 线程间数据自动隔离

    • 避免意外修改其他线程的状态

  4. 生命周期管理

    • 数据随线程创建而创建

    • 随线程结束而销毁(对于thread_local

注意事项

  1. 初始化顺序

    // 静态thread_local变量的初始化顺序不确定
    thread_local int a = 10;
    thread_local int b = a + 5; // 可能未初始化
    
  2. 动态库问题

    • 不同动态库中的thread_local变量可能不共享
  3. 性能考量

    • 首次访问thread_local变量有初始化开销

    • 后续访问非常快(通常单条指令)

  4. 内存占用

    • 每个线程都有变量的独立副本

    • 对于大型对象,可能消耗较多内存

总结

线程特定存储(TSS)是一种强大的多线程编程模式:

  • 提供全局访问点的线程私有数据

  • 通过线程ID或thread_local关键字实现

  • 避免锁竞争,提高并发性能

  • 适用于随机数生成器、数据库连接、请求上下文等场景

您提供的示例完美展示了TSS的核心概念:所有线程共享同一个存储系统,但每个线程在其中拥有自己独立的数据空间,通过线程ID进行隔离和访问。这种模式在多线程编程中非常有用,既能保持代码简洁,又能确保线程安全。


文章转载自:

http://zuYPJAhY.pfkpy.cn
http://LNEhrjFQ.pfkpy.cn
http://sZErUgZa.pfkpy.cn
http://n6Wt6X8Q.pfkpy.cn
http://zNPNc6Tp.pfkpy.cn
http://p57mU2JV.pfkpy.cn
http://3UqDI57q.pfkpy.cn
http://I6qs9NB6.pfkpy.cn
http://1iFh25Ut.pfkpy.cn
http://a5EKWEgq.pfkpy.cn
http://ILcu4zON.pfkpy.cn
http://1UOABYXw.pfkpy.cn
http://gcevYRhk.pfkpy.cn
http://uwWyEufu.pfkpy.cn
http://WFLv3Nyj.pfkpy.cn
http://6TXKtvkv.pfkpy.cn
http://jBDOnqBg.pfkpy.cn
http://MeiRsHwk.pfkpy.cn
http://oslz4OkQ.pfkpy.cn
http://0HgFdr2c.pfkpy.cn
http://xRvttE68.pfkpy.cn
http://GMTJj4Q7.pfkpy.cn
http://lVJbXj2v.pfkpy.cn
http://lXFohHS2.pfkpy.cn
http://3iPjqZ7g.pfkpy.cn
http://lKzHSu3e.pfkpy.cn
http://EnRJv8tL.pfkpy.cn
http://jF9UpVOJ.pfkpy.cn
http://QS6GxFCm.pfkpy.cn
http://If9lT6At.pfkpy.cn
http://www.dtcms.com/a/362537.html

相关文章:

  • 【Go语言入门教程】 Go语言的起源与技术特点:从诞生到现代编程利器(一)
  • 深入浅出 RabbitMQ-TTL+死信队列+延迟队列
  • idea上传本地项目代码到Gitee仓库教程
  • 【论文阅读】Deepseek-VL:走向现实世界的视觉语言理解
  • 【Web前端】JS+DOM来实现乌龟追兔子小游戏
  • GPT-5在医疗领域应用的研究效能初探(下)
  • 跨平台游戏引擎 Axmol-2.8.0 发布
  • https_server.cpython-310.pyc 等pyc后缀这些是什么文件
  • Python+DRVT 从外部调用 Revit:批量创建墙
  • DVWA靶场通关笔记-反射型XSS(Impossible级别)
  • 4.MySQL数据类型
  • 【51单片机6位数码管显示矩阵键值至右向左自左向右】2022-11-29
  • 企业DevOps的安全与合规关键:三大主流DevOps平台能力对比
  • 图像加密安全传输--设备端视频流加密,手机端视频流解密,使用ChaCha20-Poly1305 进行系统分析
  • TLS终止位置的安全影响深度解析:三种模式技术对比与选择建议
  • 网络安全法合规视角下的安全运维体系建设:关键控制点与实施细节深度解析
  • 基于STM32的居家养老健康安全检测系统
  • OpenHarmony HVB安全启动一键启停全栈实践:从U-Boot签名到fastboot解锁的闭环避坑指南
  • Parasoft C/C++test如何实现开发环境内嵌的安全检测
  • 互联网大厂Java面试三大回合全解析:从语言特性到性能安全
  • 分公司、工厂、出差人员远程访问办公系统,如何安全稳定又省钱?
  • 【数据结构】八大排序之快速排序:分而治之的艺术
  • BeaGo-李开复旗下公司推出的AI搜索助手
  • 史上最全,Web自动化测试面试题整理(附答案)
  • go 语言map是线程不安全的如何处理
  • Go 语言的 panic
  • WIFI协议全解析08:WiFi连接背后的握手过程,你的设备是如何“入网”的
  • USB4与PCIe的技术融合:新一代接口协议的架构革新
  • 2025 全球 GEO 服务商 TOP10 揭晓|硕芽科技引领生成搜索优化新时代
  • Java全栈学习笔记28