线程本地存储 TLS
2018-02-27 14:22
369 查看
线程本地存储 TLS(静态TLS)
TLS的概念,以下链接讲的比较详细
静态TLS
TLS的应用
总结
__thread 只能用于修辞POD类型,不能修饰class类型,因为无法自动调用构造函数和析构函数。
在VC++下,__declspec 可以用于修饰对象,能调用对象的构造函数和析构函数,如下示例代码:
线程结束时会分别调用对象的构造及析构函数
windows下TSL 详细见如下链接:
https://msdn.microsoft.com/en-us/library/6yh4a9k1.aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686749(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/ms686991(v=vs.85).aspx" target=_blank>https://msdn.microsoft.com/en-us/library/ms686991(v=vs.85).aspx
C++11提供了thread_local关键字实现了线程存储
https://stackoverflow.com/questions/6021273/how-to-allocate-thread-local-storage
http://en.cppreference.com/w/cpp/language/storage_duration
对日志时间戳的缓存:每一条日志的时间戳格式:20180101 13:00:12 123221。那么在一秒内输出多条日志时,日期和时间是不变的,变的是微秒。那么可以将格式化的时间字符串及精度为秒的时间值缓存起来。只有在跨秒时再重新格式化时间字符串。对于每一条日志,产生该日志的肯定是一个确定线程,对于格式化的时间字符串等变量对该线程来说就是线程私有的。那么对用于缓存的变量是可以用__thread声明的, 一是提高执行效率,而是不用考虑互斥的问题。
t_time[32] 是用来缓存为格式后的时间戳字符串,精度是到秒。其被声明为__thread
t_lastSecond 用存储上一秒的时间戳,精度为秒。其被声明为__thread
以下是muduo日志库中时间戳生成的代码
对线程ID的缓存,每个线程的ID都是唯一的,私有的。所以非常适合用__thread修辞。在muduo库中有对thread id的缓存及对格式化的缓存。定义在CrrentThread命名空间中,都用 __thread修辞。代码如下:
在muduo的日志库中,每条消息都会记录产生该消息的对应的thread id,所以将thread id及格式化的字符串缓存起来也可以提高日志性能。
- 线程私有的对象,如文件fd,socket链接。计数,缓存等。
- 还可以修饰那些”值可能会变,带有全局性,但是又不值得用全局锁保护“的变量。
TLS的概念,以下链接讲的比较详细
静态TLS
TLS的应用
总结
线程本地存储 TLS(静态TLS)
TLS的概念,以下链接讲的比较详细
聊聊Linux中的线程本地存储(1)——什么是TLS静态TLS
上面链接中提到__thread关键字是GCC编译器的扩展,并不属于标准,所以在window下的VC++是没有这个关键字的。不过VC++也提供一个对应的关键字 __declspec来声明线程变量。如下:__declspec( thread ) int tls_i = 1;
__thread 只能用于修辞POD类型,不能修饰class类型,因为无法自动调用构造函数和析构函数。
在VC++下,__declspec 可以用于修饰对象,能调用对象的构造函数和析构函数,如下示例代码:
hljs cpp">#include <thread> class T { public: T():v(0) { std::cout << "create t" << std::endl; } ~T() { std::cout << "delete t" << std::endl; } void PrintValue() { std::cout << "=========> v:"<< v << std::endl; } void SetValue(int i) { v = i; } private: int v; }; __declspec(thread) T t1; void test() { t1.PrintValue(); } void test1() { t1.SetValue(16); } int main() { std::thread tt1(test1); std::thread tt2(test); system("pause"); }
线程结束时会分别调用对象的构造及析构函数
windows下TSL 详细见如下链接:
https://msdn.microsoft.com/en-us/library/6yh4a9k1.aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686749(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/ms686991(v=vs.85).aspx" target=_blank>https://msdn.microsoft.com/en-us/library/ms686991(v=vs.85).aspx
C++11提供了thread_local关键字实现了线程存储
https://stackoverflow.com/questions/6021273/how-to-allocate-thread-local-storage
http://en.cppreference.com/w/cpp/language/storage_duration
TLS的应用
在muduo的日志库中作者提到了几处优化的地方,其中有两条就是通过TLS来实现的,我觉得很有借鉴意义。对日志时间戳的缓存:每一条日志的时间戳格式:20180101 13:00:12 123221。那么在一秒内输出多条日志时,日期和时间是不变的,变的是微秒。那么可以将格式化的时间字符串及精度为秒的时间值缓存起来。只有在跨秒时再重新格式化时间字符串。对于每一条日志,产生该日志的肯定是一个确定线程,对于格式化的时间字符串等变量对该线程来说就是线程私有的。那么对用于缓存的变量是可以用__thread声明的, 一是提高执行效率,而是不用考虑互斥的问题。
__thread char t_time[32]; __thread time_t t_lastSecond;
t_time[32] 是用来缓存为格式后的时间戳字符串,精度是到秒。其被声明为__thread
t_lastSecond 用存储上一秒的时间戳,精度为秒。其被声明为__thread
以下是muduo日志库中时间戳生成的代码
void Logger::Impl::formatTime() { int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch(); time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / 1000000); int microseconds = static_cast<int>(microSecondsSinceEpoch % 1000000); if (seconds != t_lastSecond) { t_lastSecond = seconds; struct tm tm_time; ::gmtime_r(&seconds, &tm_time); int len = snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec); assert(len == 17); (void)len; } //微秒值是单独生成的 Fmt us(".%06dZ ", microseconds); assert(us.length() == 9); stream_ << T(t_time, 17) << T(us.data(), 9); }
对线程ID的缓存,每个线程的ID都是唯一的,私有的。所以非常适合用__thread修辞。在muduo库中有对thread id的缓存及对格式化的缓存。定义在CrrentThread命名空间中,都用 __thread修辞。代码如下:
namespace CurrentThread { // internal extern __thread int t_cachedTid; extern __thread char t_tidString[32]; extern __thread const char* t_threadName; void cacheTid(); inline int tid() { if (t_cachedTid == 0) { cacheTid(); } return t_cachedTid; } inline const char* tidString() // for logging { return t_tidString; } inline const char* name() { return t_threadName; } bool isMainThread(); } //初始化 namespace CurrentThread { __thread int t_cachedTid = 0; __thread char t_tidString[32]; __thread const char* t_threadName = "unknown"; const bool sameType = boost::is_same<int, pid_t>::value; BOOST_STATIC_ASSERT(sameType); } //缓存thread id void CurrentThread::cacheTid() { if (t_cachedTid == 0) { t_cachedTid = detail::gettid(); int n = snprintf(t_tidString, sizeof t_tidString, "%5d ", t_cachedTid); assert(n == 6); (void) n; } }
在muduo的日志库中,每条消息都会记录产生该消息的对应的thread id,所以将thread id及格式化的字符串缓存起来也可以提高日志性能。
总结
归纳一下TLS使用的场景:- 线程私有的对象,如文件fd,socket链接。计数,缓存等。
- 还可以修饰那些”值可能会变,带有全局性,但是又不值得用全局锁保护“的变量。
相关文章推荐
- 线程本地存储(Thread Local Storage, TLS)分析与使用
- [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)调用复制文件接口的案例]
- [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32线程和pthread线程比较]
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32线程和pthread线程比较]
- 【C++11】thread_local/TLS/线程本地存储
- 聊聊Linux中的线程本地存储(1)——什么是TLS
- 线程本地存储(TLS)
- 线程本地存储(Thread Local Storage, TLS)简单分析与使用
- 在windows下调试线程本地存储(TLS)时遇到的一个问题
- 线程本地存储(TLS)的学习
- 线程通信原理之一:线程本地存储(TLS)
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- 线程本地存储 (TLS)
- 线程本地存储 (TLS)
- TLS 线程本地存储(转)
- 线程本地存储(Thread Local Storage, TLS)简单分析与使用
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- C21、线程本地存储(TLS)