使用全局变量或者静态变量是导致多线程编程中非线程安全的常见原因。在多线程程序中,保障非线程安全的常用手段之一是使用互斥锁来做保护,这种方法带来了并发性能下降,同时也只能有一个线程对数据进行读写。
如果程序中能避免使用全局变量或静态变量,那么这些程序就是线程安全的,性能也可以得到很大的提升。
如果有些数据只能有一个线程可以访问,那么这一类数据就可以使用线程局部存储机制来处理,虽然使用这种机制会给程序执行效率上带来一定的影响,但对于使用锁机制来说,这些性能影响将可以忽略。
还有一种大致相当的编程技术就是使用 线程特有数据(没 线程局部存储 易用, 也没 线程局部存储 高效) ,这将在 线程特有数据 中讨论。
线程局部存储介绍
__thread
是GCC内置的线程局部存储设施,存取效率可以和全局变量相比。
__thread
变量每一个线程有一份独立实体,各个线程的值互不干扰。
可以用来修饰那些带有全局性且值可能变,但是又不值得用全局变量保护的变量。
__thread
使用规则:只能修饰POD类型(类似整型指针的标量,不带自定义的构造、拷贝、赋值、析构的类型,
二进制内容可以任意复制memset, memcpy, 且内容可以复原,
不能修饰class类型,因为无法自动调用构造函数和析构函数,
可以用于修饰全局变量,函数内的静态变量,不能修饰函数的局部变量或者class的普通成员变量,
且 __thread
变量值只能初始化为编译器常量
( 例如 : 值在编译器就可以确定const int i=5,运行期常量是运行初始化后不再改变const int i=rand() ).
一个简单例子
1 |
|
程序输出 :
6
6 //可见__thread值线程间互不干扰
10
如何使用线程局部存储技术来实现函数的线程安全
我们先讨论一下非线程安全的 stderror()
的实现, 接着说明如何使用线程局部存储技术来实现该函数的线程安全.
非线程安全的stderror()
An implementation of strerror() that is not thread-safe.
1 | /*************************************************************************\ |
线程安全的stderror()
这是使用线程局部存储技术实现的线程安全的stderror().
如果对使用线程特有数据技术实现的线程安全的stderror()感兴趣,
请转 线程特有数据
1 | /*************************************************************************\ |