Reentrant和Thread-safe

在单线程程序中,整个程序都是顺序执行的,一个函数在同一时刻只能被一个函数调用,但在多线程中,由于并发性,一个函数可能同时被多个函数调用,此时这个函数就成了临界资源,很容易造成调用函数处理结果的相互影响,如果一个函数在多线程并发的环境中每次被调用产生的结果是不确定的,我们就说这个函数是"不可重入的"/"线程不安全"的。为了解决这个问题,POSIX多线程库提出了一种机制,用来解决多线程环境中的线程数据私有化问题,这套机制的主要思想是利用同步和互斥维护一个同名不同值的表,这个表会维护每个线程自己的资源地址,表面上是同一个变量,实质上这个变量在不同的线程中的地址是不一样,这样就保证了每个线程其实都在使用自己的资源,实现了"thread-safe"。

其实,随着多线程程序的逐渐流行,除了这种利用系统机制保护线程私有数据的方法,还有一部分人重新编写了一些多线程库函数,这些函数的主要特点就是实现了算法和数据的分离,函数内部只负责实现算法,需要的数据由线程传入,这样就保证了函数的多线程安全,eg

char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf); //这个就是asctime的thread-safe版,有_r后缀

但由于接口不同,完全重写的函数推广尚需时日。

当下用的更多的是使用_REENTRANT来在原来的函数的基础上改造,如果编译的时候定义了这个宏,相关的库函数就会被编译成"thread-safe"的版本。

模型

如果要查看这些函数的man手册,可以安装相关的man手册

pthread_key_t key           //创建用于保护线程私有资源的key
pthread_once_t once_key //创建用于初始化key的once_key,要求用PTHREAD_INIT_ONCE来赋值,否则结果不确定
pthread_key_create() //创建key
pthread_once() //初始化key
pthread_getspedifc() //从key表中获得线程私有资源的地址
pthread_setspedifc() //将线程私有资源的地址放到key中
...

例子

表面上每个函数调用了reverse()都会得到rev的地址,其实这个rev地址在不同的线程中并不相同,一旦一个线程调用了reverse()函数,函数首先会到key标识的表中去搜索这个线程以前是否调用过这个函数,如果调用过,就将表中属于这个线程的rev地址返回,如果没有,就分配rev,并将该线程和它的专属rev地址注册到表中,这样就把reverse()打造成了一个可重入的函数。

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
pthread_key_t key;
pthread_once_t once_key=PTHREAD_ONCE_INIT;
#ifdef _REENTRANT
void myDestructor(void*p){
free(p);
}
void myCreateKey(void){
//创建key
pthread_key_create(&key,myDestructor);
}
#endif char* reverse(char* buf,int len){
#ifdef _REENTRANT
//初始化key
pthread_once(&once_key,myCreateKey); //从key中获取一个thread-specific的数据
char* rev=(char*)pthread_getspecific(key);
if(NULL==rev){
rev=(char*)malloc(len+1);
//将thread-specific的数据放到key中
pthread_setspecific(key,rev);
}
#else
static char rev[100];
#endif
bzero(rev,sizeof(rev));
//翻转buf
while(len--)
rev[len]=*buf++;
return rev;
} void* fcn1(void* p){
while(1){
char buf[100]="123456789";
printf("[%lu]:%s\n",pthread_self(),buf);
char* rev=reverse(buf,strlen(buf));
sleep(1);
printf("[%lu]:%s\n",pthread_self(),rev);
} } void* fcn2(void* p){
while(1){
char buf[100]="abcdef";
printf("[%lu]:%s\n",pthread_self(),buf);
char* rev=reverse(buf,strlen(buf));
sleep(2);
printf("[%lu]:%s\n",pthread_self(),rev); }
} int main(int argc, const char *argv[])
{
pthread_t tid[4];
pthread_create(&tid[0],NULL,fcn1,NULL);
pthread_create(&tid[1],NULL,fcn2,NULL);
pause();
return 0;
}

Linux 多线程可重入函数的更多相关文章

  1. 【Linux】可重入函数和线程安全的区别与联系【转】

    转自:http://blog.csdn.net/scenlyf/article/details/52074444 版权声明:本文为博主原创文章,未经博主允许不得转载. *****可重入函数 函数被不同 ...

  2. linux: 可重入函数与不可重入函数

    1. 可重入函数与线程安全 摘自 多线程和多进程的区别(小结) http://blog.csdn.net/hairetz/article/details/4281931 要确保函数线程安全,主要需要考 ...

  3. [Linux]不可重入函数

    一.概述 怎么会有可重入和不可重入. 在多任务系统下,中断可能在任务执行的任何时间发生:如果一个函数的执行期间被中断后,到重新恢复到断点进行执行的过程中,函数所依赖的环境没有发生改变,那么这个函数就是 ...

  4. Linux可重入函数和线程安全的区别与联系(转)

    *****可重入函数 函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入. 当程序运行到某一个函数的时候,可能因为硬件中断或者异常而使得在用户正在执行的代码暂时终端转而 ...

  5. linux系统编程之信号(四):alarm和可重入函数

    一,alarm() 在将可重入函数之前我们先来了解下alarm()函数使用: #include <unistd.h> unsigned int alarm(unsigned int sec ...

  6. 可重入函数、线程安全、volatile

    一. POSIX 中对可重入和线程安全这两个概念的定义: Reentrant Function:A function whose effect, when called by two or more  ...

  7. (转载)可重入函数(reentrant function)

    (转载)http://blog.163.com/xu_jin_rong/blog/static/1491966220086775017178 由于cublog系统的缘故,将前段时间写的一篇blog文章 ...

  8. Linux--线程安全与可重入函数的异同

    线程安全 比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成: 1. 在 Items[Size] 的位置存放此元素: 2. 增大 Size 的值. 在单线程运行的情况下,如果 ...

  9. C语言可重入函数和不可重入函数

    可重入函数和不可重入函数的概念 在函数中如果我们使用静态变量了,导致产生中断调用别的函数的 过程中可能还会调用这个函数,于是原来的 静态变量被在这里改变了,然后返回主体函数,用着的那个静态变量就被改变 ...

随机推荐

  1. 去哪儿搜索引擎QSearch设计与实现

    本次演讲主要介绍的是QSearch的具体设计和应用场景,并分别解答以下几个问题:QSearch与开源垂直软件Lucene的区别:具体业务的实现技巧.以及QSearch的具体设计:如何通过Partial ...

  2. Sql Server函数全解(三)数据类型转换函数和文本图像函数

    一:数据类型转换函数 在同时处理不同数据类型的值时,SQL Server一般会自动进行隐士类型转换.对于数据类型相近的值是有效的,比如int和float,但是对于其它数据类型,例如整型和字符类型,隐士 ...

  3. hotcss用法

    1.0 GitHub下载地址: https://github.com/imochen/hotcss 或者我的百度网盘:http://pan.baidu.com/s/1pKlLqHX 使用方法看demo ...

  4. 轻量级ORM

    不知道可能称的上是ORM,其实就是一个DBHelper.看到网上不少人写自己的ORM,但我觉得都不太好. 我这个ORM,学习成本非常低,常用的方法仅有几个,在使用的过程中,你不需要有太多要注意的地方, ...

  5. 怎么搭建EF的环境?(Entity Framework)

    1.EF是什么? EF是.net封装的一个用于数据库交互的实体层框架,它的全称是Entity Framework. 2.EF搭建: 新建之后,我们就可以看到里面的内容: 我们可以分别看一下它里面有些什 ...

  6. 【C#】委托-Delegate

    C# 委托(Delegate) C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针.委托(Delegate) 是存有对某个方法的引用的一种引用类型变量.引用可在运行时被改变. 委托 ...

  7. AJAX与PHP(PHP笔记)--动态验证用户名

    在PHP基础的学习过程中经常会遇到对页面的局部刷新. 比如说,我们在填写用户名的同时,对数据库中的信息进行验证,检查是否充分. 这时就要用到AJAX实现页面的动态加载. 下面例子是简单的PHP与AJA ...

  8. Linux Ctrl+c与ctrl+z的区别

    提问:CTRL-Z和CTRL-C区别?回答:CTRL-Z和CTRL-C都是中断命令,但是他们的作用却不一样.CTRL-C是强制中断程序的执行,而CTRL-Z的是将任务中断,但是此任务并没有结束,他仍然 ...

  9. java web学习总结(八) -------------------HttpServletResponse对象(二)

    一.HttpServletResponse常见应用--生成验证码 1.1.生成随机图片用作验证码 生成图片主要用到了一个BufferedImage类,

  10. Struts2入门(一)——环境搭建和简单例子(Struts2 2.5.2版本)

    一.前言 1.了解三大框架 什么是框架? 框架是一种规范,一种规则,一种把技术组织起来的规则,这就是框架. 什么是三大框架(SSH),Struts.hibernate和spring的作用是什么? St ...