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 //创建用于保护线程私有资源的keypthread_once_t once_key //创建用于初始化key的once_key,要求用pthread_init_once来赋值,否则结果不确定pthread_key_create() //创建keypthread_once() //初始化keypthread_getspedifc() //从key表中获得线程私有资源的地址pthread_setspedifc() //将线程私有资源的地址放到key中...
例子
表面上每个函数调用了reverse()都会得到rev的地址,其实这个rev地址在不同的线程中并不相同,一旦一个线程调用了reverse()函数,函数首先会到key标识的表中去搜索这个线程以前是否调用过这个函数,如果调用过,就将表中属于这个线程的rev地址返回,如果没有,就分配rev,并将该线程和它的专属rev地址注册到表中,这样就把reverse()打造成了一个可重入的函数。
#include#include#include#includepthread_key_t key;pthread_once_t once_key=pthread_once_init;#ifdef _reentrantvoid mydestructor(void*p){ free(p);}void mycreatekey(void){ //创建key pthread_key_create(&key,mydestructor);}#endifchar* 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;}
基于Zynq SoC的ADALM专业射频学习模块
400ZR/OpenZR+相干可插拔光模块剖析
基于模糊行为和神经网络的机器人视觉伺服控制方案
AMD展示嵌入式处理器的研发成果
南方电网公司将全力推动智能电网运营商的高质量发展
Linux 多线程可重入函数
MOS管导通损耗的计算方法
晶圆代工龙头台积电被爆出内部员工盗取资料案
AirPods 2:有望在3月25日正式发布
两部门印发《目录》,多款连接器产品“军转民”
Littelfuse为AEC-Q101标准车用二极管增添两个新系列
比特币价格跌破4000美元关口 跌幅登历史高位
凌阳(SUNPLUS)推出基于SPMC75的LIN结点方案
瑕疵检测系统将会成为纺织产业日后发展的必然趋势
减少电子医疗设备EMI问题的设计方法
800V高压超充技术兴起,国内SiC MOSFET产业谁受益?
大比特资讯将在深圳举办5G基站电源技术创新研讨会
高通推aptX Voice音频技术,可提升蓝牙通话质量
汽车成像雷达波形选择
穿越到深圳,来现场感受铁电存储的魅力