线程安全、线程私有变量

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

线程安全、线程私有变量_第1张图片

看下面这个例子:
线程安全、线程私有变量_第2张图片



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



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



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


这下就解决了刚才的问题。
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));
这些函数就是创建线程私有变量的函数。
线程安全、线程私有变量_第7张图片

在进程中会维护一个表,里面共有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。
线程安全、线程私有变量_第8张图片

另一个线程也是这样通过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。
线程安全、线程私有变量_第9张图片

还有一个最重要的是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
线程安全、线程私有变量_第10张图片
线程安全、线程私有变量_第11张图片


线程安全、线程私有变量_第12张图片

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

线程安全、线程私有变量_第15张图片



你可能感兴趣的:(线程)