Linux线程局部变量实现
什么是线程局部变量,就是每个线程各自拥有一个的变量;比如
errno
,是每个线程各自拥有的全局变量;在代码实践当中来说,这种技术还是很有用的。C
语言中实际上由很多强大的函数,比如:__sync
函数族、pthread_once
函数,C++
中的原子函数,GCC
就已经帮我们实现了;当然这些函数很多是依赖于GCC
编译器或者某个特定库的,需要注意。
错误码errno
的实现
errno
是Linux
中用来表述错误的变量,int
类型,是一个全局变量,但是在使用当中我们却不需要考虑多线程的问题;errno
算不上很好的设计,但是也难找到更好的了
查看/usr/include/errno.h
中的定义:
/* The error code set by various library functions. */
extern int *__errno_location (void) __THROW __attribute_const__;
# define errno (*__errno_location ())
到这里可以看到实际上errno
是一个函数的返回值,并且是指针解引用获得的;从这里笔者不能看出什么,但是也没能找到源码;就不做深究,因为接下来笔者会介绍一些局部变量的实现原理以及实现方式
pthread_once
函数
单例模式的实现方式由很多种,但是当遭遇到多线程时就较为复杂,如果在
main
函数中可以统一初始化,但是当我们写的时动态库时就不太方便;pthread_once
就可以保证其指定的函数在多个线程中仅执行一次
extern int pthread_once (pthread_once_t *__once_control,
void (*__init_routine) (void)) __nonnull ((1, 2));
#include <iostream>
#include <pthread.h>
#include <thread>
void debug_pthread_once()
{
std::cout << "pthread once" << std::endl;
}
int main() {
pthread_once_t once = 0;
std::thread th1([&once](){
for (int i = 0; i < 10; i++)
pthread_once(&once, debug_pthread_once);
});
std::thread th2([&once](){
for (int i = 0; i < 10; i++)
pthread_once(&once, debug_pthread_once);
});
th1.join();
th2.join();
return 0;
}
pthread once
测试可以验证,pthread_once
确实仅仅执行了一次
参数说明
pthread_once_t
这个参数定义为int
有三种状态:NEVER(0)
、IN_PROGRESS(1)
、DONE(2)
;如果once
被设置为2
就不会被执行,为1
则会阻塞。从这段代码来说,以基础库也可以实现类似的效果,但是需要加锁。
pthread_key_create
函数
该函数可以辅助实现类似于errno这样的线程全局变量,在使用时和全局变量没有区别,但是每个线程独有
函数原型
#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);
使用方法
例子来源于man说明
static pthread_key_t key; //全局变量
static pthread_once_t key_once = PTHREAD_ONCE_INIT; //pthread_once
static void
make_key()
{
(void) pthread_key_create(&key, NULL);
}
func()
{
void *ptr;
(void) pthread_once(&key_once, make_key); //多个线程,但是只需要创建一个key
if ((ptr = pthread_getspecific(key)) == NULL) { //获取线程独享数据
ptr = malloc(OBJECT_SIZE);
...
(void) pthread_setspecific(key, ptr); //取出线程独享数据
}
...
}
-
pthread_key_create
:第二个参数可以指定线程退出时执行的函数,其参数就是pthread_setspecific
设置的指针 -
pthread_key_delete
:并不会释放资源
errno
的代码实现
使用以上函数实现一个类似于
errno
的功能
#include <iostream>
#include <pthread.h>
#include <thread>
#include <unistd.h>
static pthread_key_t key; //全局变量
static pthread_once_t key_once = PTHREAD_ONCE_INIT; //pthread_once
void key_exit(void *ptr)
{
printf("errno: %u %d\n", ptr, *(int*)ptr);
free(ptr);
}
static void make_key()
{
(void) pthread_key_create(&key, key_exit);
}
int *__errno_location(void)
{
pthread_once(&key_once, make_key);
void *ptr = pthread_getspecific(key);
if (!ptr) {
ptr = malloc(sizeof(int));
pthread_setspecific(key, ptr);
}
return (int*)ptr;
}
int main() {
std::thread th1([](){
for (int i = 1; i < 5; i++) {
*__errno_location() = i;
usleep(20000);
}
});
std::thread th2([](){
for (int i = 5; i < 10; i++) {
*__errno_location() = i;
usleep(20000);
}
});
std::thread th3([](){
for (int i = 11; i < 15; i++) {
*__errno_location() = i;
usleep(20000);
}
});
th1.join();
th2.join();
th3.join();
return 0;
}
errno: 3019901728 14
errno: 2952792864 4
errno: 2885684000 9
网友评论