调试经验--线程安全

来源:互联网 发布:win10中的keeper软件 编辑:程序博客网 时间:2024/06/14 20:49

 调试经验--线程安全


    有一次调试程序,遇到一个bug,怎么也分析不清楚问题,最后通过一步步的打印信息跟踪分析,竟然发现问题出在调用的C语言库函数中!

    当时是在调试网络对时功能(ntp),对时之后,设置系统时间,并且写入实时时钟(RTC)。具体逻辑在这里就不说明了,问题的核心就在于程序中有两个线程都调用了localtime()函数。该函数形式如下:
 struct tm *localtime(const time_t *clock);
其返回的值是一个指针,指针指向的对象是一个静态变量!a线程调用localtime(),获得指向tm对象的指针;b线程也调用一次localtime(),则其值变化;此时a线程使用其返回值进行判断,获得错误结果!!!

    这个事情对我是一个警醒:通常在一次bug寻找过程中,我对于遇到的问题,首先是考虑自己新添加的代码是否有问题,然后是怀疑近期别人的代码是否有问题,再然后是怀疑是否早期提交的代码就隐含有问题。我很少怀疑到C语言库函数有问题!

    由于这个问题,我重点关注了下线程安全与可重入的相关资料,记录如下,为以后备查:

 

   线程安全与可重入

    C  run-time library里面的一些函数使用了全局变量和静态变量,是多线程不安全函数。

   我们的代码中使用的线程不安全函数如下: 

asctime,gethostbyname,getnetbyname,inet_ntoa,localtime,readdir,strerror

 其中localtime()有时会导致严重问题,导致程序逻辑判断错误。
解决办法,使用相应的线程安全的localtime_r(),其调用形式如下:
struct tm* localtime_r( const time_t* timer, struct tm* result );
其中第二个参数 result 指向用户申请好的tm对象,返回值指针也指向同一个对象,从而避免了指向C库内同一个静态tm对象的问题。

 
判断一个函数是否线程安全不是一件很容易的事情。但是我们可以通过下面这几条确定一个函数是线程不安全的。

a, 函数中访问全局变量和堆。
b, 函数中分配,重新分配释放全局资源。
c, 函数中通过句柄和指针的不直接访问。
d, 函数中使用了其他线程不安全的函数或者变量。

因此在编写线程安全函数时,要注意两点:

1, 减少对临界资源的依赖,尽量避免访问全局变量,静态变量或其它共享资源,如果必须要使用共享资源,所有使用到的地方必须要进行互斥锁 (Mutex) 保护;
2, 线程安全的函数所调用到的函数也应该是线程安全的,如果所调用的函数不是线程安全的,那么这些函数也必须被互斥锁 (Mutex) 保护;

 

一个函数想要成为可重入的函数,必须满足下列要求:

a) 不能使用静态或者全局的非常量数据;
b) 不能够返回地址给静态或者全局的非常量数据;
c) 函数使用的数据由调用者提供;
d) 不能够依赖于单一资源的锁;
e) 不能够调用非可重入的函数。 

 

如果一个函数是可重入的,那么它肯定是线程安全的。但反之未然,一个函数是线程安全的,却未必是可重入的。程序开发人员应该尽量编写可重入的函数。

 

0 0
原创粉丝点击