一,什么是线程安全?
所谓的线程安全是指在多线程中,即使有多个进程访问同一份代码也不会造成不确定的结果。
即:多个线程完成一个任务与一个线程去完成的结果总是一样的,并且不会产生预料之外的结果,那么这样的线程便是安全的。
二,怎么保证线程是安全的?
对于多线程的程序访问冲突的问题十分常见,而最简单的方法便是引入互斥锁,通过互斥锁可以实现拿到锁的线程可以正常执行,而没有拿到的,只有等待,不能访问共享资源,直到锁被释放。
eg:请看如下代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int count=0;
void* myPthread1(void)
{
int i=0;
int val=0;
for(;i<5000;i++)
{
val=count;
printf("pthread id=%ld,count=:%d\n",(unsigned long)pthread_self(),count);
count=val+1;
}
}
int main()
{
pthread_t p1,p2;
pthread_create(&p1,NULL,(void*)myPthread1,NULL);
pthread_create(&p2,NULL,(void*)myPthread1,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
printf("count=%d\n",count);
return 0;
}
pthead_create()函数是用来建立一个线程的,如上可得我们创建了两个线程分别为p1和p2,然后用这两个线程分别计算从0加到5000的值,很明显最后答案的输出count会是10000,那么实际情况呢?
如下是Linux下运行得到的结果
第一次,
第二次:
其实不管试多少次我们都会发现结果很少会是正确,而这便是线程安全的问题了,当两个线程不加限制的去访问val时可能拿到的并不是当前正确的值,例如当a线程刚拿到val时,而b线程可能正在拿之前val进行计算,因此最后便导致了结果的不确定性。
为了解决问题,我们加入互斥锁,
如下为改造后的代码:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int count=0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //申请一个公共锁
void* myPthread1(void)
{
int i=0;
int val=0;
for(;i<5000;i++)
{
//在计算时,先加锁
pthread_mutex_lock(&mutex);
val=count;
printf("pthread id=%lld,count=:%d\n",(unsigned long long)pthread_self(),count);
count=val+1;
pthread_mutex_unlock(&mutex);
//访问完毕后,释放锁
}
}
int main()
{
pthread_t p1,p2;
pthread_create(&p1,NULL,(void*)myPthread1,NULL);
pthread_create(&p2,NULL,(void*)myPthread1,NULL);
pthread_join(p1,NULL); //线程的等待
pthread_join(p2,NULL);
printf("count=%d\n",count);
return 0;
}
如下为改造后的结果:
3:
4:
通过加锁的方式,现在便可以实现,线程每次的操作都会是原子的,所以结果将会一直是10000.
三,什么是可重入函数?
可重入函数:
在 实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果这个函数不幸被设计成为不可重入的函数的话,那么不同任务调用这个函数时可能修改其他任 务调用这个函数的数据,从而导致不可预料的后果。那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会 出错。不可重入函数在实时系统设计中被视为不安全函数。
满足下列条件的函数多数是不可重入的:
(1)函数体内使用了静态的数据结构;
(2)函数体内调用了malloc()或者free()函数;
(3)函数体内调用了标准I/O函数。
如何写出可重入的函数?
在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用缺省态(auto)局部变量,写出的函数就将是可重入的。如果必须访问全局变量,记住利用互斥信号量来保护全局变量。或者调用该函数前关中断,调用后再开中断。
可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。可重入函数或者只使用局部变量,即保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护。
四,线程安全与可重入函数的对比
可重入函数与线程安全的区别与联系:
(1)线程安全是在多个线程情况下引发的,而可重入函数可以在只有一个线程的情况下。
(2)线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
(3)如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
(4)如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。
(5)线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响使结果是相同的。