线程安全、线程私有变量

时间:2021-02-01 18:16:00

线程安全:无论有多少个线程调用这个函数,此函数都会给相应的线程返回正确的结果。

线程安全、线程私有变量

看下面这个例子: 线程安全、线程私有变量

线程安全、线程私有变量


线程安全、线程私有变量

很明显我们在rebuf这个函数里返回了一个局部变量,在rebuf这个函数运行完之后,局部变量就会被销毁,所以我们打印出来的是这个样子的。 那我们改进呢,函数运行完之后,局部变量就会被销毁,那我们就不让它销毁,加上static关键字 线程安全、线程私有变量

线程安全、线程私有变量


线程安全、线程私有变量

这次编译运行后发现打印出来的和我想要的不一样,返回的地址空间被污染了,导致另一个线程得到了错误的结果。 我们在来改进一下,既然两个线程,两次分别调用使用的是返回值rbuf的同一个空间,那么我们就让两次调用分别使用不同的地址空间,使用malloc。 线程安全、线程私有变量


线程安全、线程私有变量

哈哈,这样不就好了。但是这样有一个问题,现在我有两个线程调用这个函数,就申请两次空间,我要是有一百个线程调用这个函数,岂不是申请100次?,所以我们创建的完了还要释放掉,当然是在printf()函数使用完之后才能free()。 对于我们自己写的函数我们可以这么做,那么在之前没有线程这个概念的时候呢,进程之间的数据是相互不影响的,那时候是实现的系统调用和库函数难道就用不了吗? 当然不是,接下来的例子就是来解决这一问题的 线程安全、线程私有变量
线程安全、线程私有变量


线程安全、线程私有变量

这下就解决了刚才的问题。 int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)); int pthread_key_delete(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *pointer); void * pthread_getspecific(pthread_key_t key); pthread_once_t once_control = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); 这些函数就是创建线程私有变量的函数。 线程安全、线程私有变量

在进程中会维护一个表,里面共有128个键值,每个键值都有一个标志位(假如标志0为没占用,1为占用),还有析构函数。每个线程也对应一张表如上图。 先定义一个全局变量pthread_key_t mykey; 先调用这个函数int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));这个函数会给key设置一个没有用过的最小的键值,把响应标志位置位1,如上图就是分配的零。 然后传入键值,void * pthread_getspecific(pthread_key_t key);如果mykey之前没有被地址,这个函数会返回NUU。然后我们就可以创建一个指针P,给它申请空间,int pthread_setspecific(pthread_key_t key, const void *pointer);通过这个函数使P与键值进行绑定,下次就可以通过键值来使用线程申请的次有空间了。如果调用void * pthread_getspecific(pthread_key_t key);函数时,mykey已经绑定好键值了,那么就会这个函数就会返回键值所绑定的地址P。 线程安全、线程私有变量

另一个线程也是这样通过mykey得到0,但是“你的0”和“我的0”是不一样的,此0非彼0,他会拿到这个“0”通过int pthread_setspecific(pthread_key_t key, const void *pointer);通过这个函数使申请的Q空间与键值进行绑定。下次再调用void * pthread_getspecific(pthread_key_t key);函数时,mykey已经绑定好地址了,那么就会这个函数就会返回键值所绑定的地址Q。 线程安全、线程私有变量

还有一个最重要的是int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));这个函数只能调用一次,如果调用第二次的话会再设置一个没有用过的最小的键值,也就是1,这个key就不是我们需要的了。所以通过调用pthread_once_t once_control = PTHREAD_ONCE_INIT;int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));这个函数使pthread_key_create只被运行一次。线程退出后会检测mykey的标志位有没有效,有效则调用析构函数来释放资源。 当然这个方法是适用于多线程的,单线程完全没有必要这样,只会花费更多的开销。所以我们可以通过编译宏的方式,来采用运行方式。gcc -o xxx xxx.c -lpthread -D__REENTRANT 线程安全、线程私有变量
线程安全、线程私有变量
线程安全、线程私有变量

线程安全、线程私有变量

线程安全、线程私有变量
还有一个方法就是定义rbuf是加上修饰__thread,这个方法好尼玛简单啊,但是有什么缺点就不做探讨了。当然,上面那个方法既然存在就有他的道理。 线程安全、线程私有变量

线程安全、线程私有变量

线程安全、线程私有变量


线程安全、线程私有变量