线程本地存储
本节阅读量:在多线程程序中,有时候我们希望每个线程都拥有自己独立的数据副本,而不是与其他线程共享。这就好比每个人都有自己的储物柜,放进去的东西只有自己能拿到,互不干扰。
为什么需要线程本地存储
考虑这样一个场景:我们需要统计每个线程处理了多少个任务。
|
|
这个程序的问题是:
task_count被所有线程共享,存在数据竞争- 每个线程输出的 “processed X tasks” 并不是自己实际处理的任务数
- 最后输出的总数可能也不正确
当然,我们可以用锁来保护 task_count,但这样每个线程输出的仍然不是自己想要的结果——它们看到的是共享的累计值,而非各自的贡献。
使用 thread_local
C++11 引入了 thread_local 关键字,用于声明线程本地存储的变量。每个线程都有自己独立的副本,互不影响。
|
|
输出:
|
|
每个线程都有自己的 task_count,修改时互不干扰。主线程的 task_count 保持初始值 0,因为它也有自己的副本。
thread_local 的生命周期
thread_local 变量的生命周期与线程绑定:
- 创建时机:当线程首次使用到
thread_local对象时(首次引用/调用),才会初始化对应的对象 - 销毁时机:当线程结束时,该线程的
thread_local变量副本会被销毁
|
|
输出:
|
|
典型应用场景
1. 线程专属的缓存
|
|
2. 线程专属的日志记录器
|
|
最佳实践
- 优先使用
thread_local:当数据确实不需要在线程间共享时,使用thread_local比使用锁更高效 - 注意初始化时机:
thread_local变量在第一次使用时初始化,而非线程创建时 - 避免过多内存占用:每个线程都有独立副本,大量
thread_local变量会增加内存消耗 - 配合 RAII 使用:
thread_local对象会在线程结束时自动析构,适合管理线程专属资源 - 避免在 thread_local 中存储指向其他线程 thread_local 的引用
本节目录