关于函数重入和线程安全之我讨论(一)
来源:互联网 发布:如果一战德国胜利 知乎 编辑:程序博客网 时间:2024/05/16 08:35
线程安全:一个函数被称为线程安全的(thread-safe),当且仅当被多个并发进程反复调用时,它会一直产生正确的结果。如果一个函数不是线程安全的,我们就说它是线程不安全的(thread-unsafe)。我们定义四类(有相交的)线程不安全函数。
第1类:不保护共享变量的函数
将这类线程不安全函数变为线程安全的,相对比较容易:利用像P和V操作这样的同步操作来保护共享变量。这个方法的优点是在调用程序中不需要做任何修改,缺点是同步操作将减慢程序的执行时间。
第2类:保持跨越多个调用的状态函数
一个伪随机数生成器是这类不安全函数的简单例子。
unsigned int next = 1; int rand(void){ next = next * 1103515245 + 12345; return (unsigned int) (next / 65536) % 32768;} void srand(unsigned int seed){ next = seed;}
rand函数是线程不安全的,因为当前调用的结果依赖于前次调用的中间结果。当我们调用srand为rand设置了一个种子后,我们反复从一个单线程中调用rand,我们能够预期一个可重复的随机数字序列。但是,如果有多个线程同时调用rand函数,这样的假设就不成立了。
使得rand函数变为线程安全的唯一方式是重写它,使得它不再使用任何静态数据,取而代之地依靠调用者在参数中传递状态信息。这样的缺点是,程序员现在要被迫改变调用程序的代码。
第3类:返回指向静态变量指针的函数
某些函数(如gethostbyname)将计算结果放在静态结构中,并返回一个指向这个结构的指针。如果我们从并发线程中调用这些函数,那么将可能发生灾难,因为正在被一个线程使用的结果会被另一个线程悄悄地覆盖了。
有两种方法来处理这类线程不安全函数。一种是选择重写函数,使得调用者传递存放结果的结构地址。这就消除了所有共享数据,但是它要求程序员还要改写调用者的代码。
如果线程不安全函数是难以修改或不可修改的(例如,它是从一个库中链接过来的),那么另外一种选择就是使用lock-and-copy(加锁-拷贝)技术。这个概念将线程不安全函数与互斥锁联系起来。在每个调用位置,对互斥锁加锁,调用函数不安全函数,动态地为结果非配存储器,拷贝函数返回的结果到这个存储器位置,然后对互斥锁解锁。一个吸引人的变化是定义了一个线程安全的封装(wrapper)函数,它执行lock-and-copy,然后调用这个封转函数来取代所有线程不安全的函数。例如下面的gethostbyname的线程安全函数。
struct hostent* gethostbyname_ts(char* host){ struct hostent* shared, * unsharedp; unsharedp = Malloc(sizeof(struct hostent)); P(&mutex) shared = gethostbyname(hostname); *unsharedp = * shared; V(&mutex); return unsharedp;}
第4类:调用线程不安全函数的函数
如果函数f调用线程不安全函数g,那么f就是线程不安全的吗?不一定。如果g是类2类函数,即依赖于跨越多次调用的状态,那么f也是不安全的,而且除了重写g以外,没有什么办法。然而如果g是第1类或者第3类函数,那么只要用互斥锁保护调用位置和任何得到的共享数据,f可能仍然是线程安全的。比如上面的gethostbyname_ts。
可重入函数
可重入函数:可重入函数是线程安全函数的一种,其特点在于它们被多个线程调用时,不会引用任何共享数据。
可重入函数通常要比不可重入的线程安全函数效率高一些,因为它们不需要同步操作。更进一步说,将第2类线程不安全函数转化为线程安全函数的唯一方法就是重写它,使之可重入。
下面为rand函数的一个可重入版本
int rand_r(unsigned int* nextp){ *nextp = *nextp * 1103515245 + 12345; return (unsigned int) (*nextp / 65536) % 32768;}
显式可重入函数:如果所有函数的参数都是传值传递的(没有指针),并且所有的数据引用都是本地的自动栈变量(也就是说没有引用静态或全局变量),那么函数就是显示可重入的,也就是说不管如何调用,我们都可断言它是可重入的。
隐式可重入函数:可重入函数中的一些参数是引用传递(使用了指针),也就是说,在调用线程小心地传递指向非共享数据的指针时,它才是可重入的。例如rand_r就是隐式可重入的。
我们使用可重入(reentrant)来包括显式可重入函数和隐式可重入函数。然而,可重入性有时是调用者和被调用者共有的属性,并不只是被调用者单独的属性。
- 关于函数重入和线程安全之我讨论(一)
- 不可重入函数和线程安全
- 关于函数可重入与其线程安全之我讨论(二)
- 重入函数与线程安全
- 线程安全及重入函数
- 线程安全与不可重入函数
- 线程安全和不可重入
- 编写重入和线程安全代码
- 不可重入函数与线程安全函数
- 不可重入函数与线程安全函数
- 可重入函数,不可重入函数与线程安全
- 重入与线程安全
- 可重如函数 不可重入函数 线程安全函数 线程不安全函数
- 不可重入函数和可重入函数 线程安全性
- 关于PHP的几个版本和线程安全的讨论
- 关于可重入函数和不可重入函数
- 一分钟让你知道线程安全与不可重入的区别
- 重入和不可重入函数概念浅析
- sed原理及sed命令格式
- CopyU! v2 更新版本v2.1.442.215正式上线!
- null value was assigned to a property of primitive type setter of原因急解决方法
- Seam <s:fileUpload>标签的用法
- asp.net调试日志
- 关于函数重入和线程安全之我讨论(一)
- 国内知名产品和他们的开发语言v0.0.1
- Window下配置svn
- 机载激光雷达测量技术理论与方法读书笔记(3)机载激光雷达测量数据的滤波分类
- flex4与java通信常见问题一
- 64位jdk连接不上jdbc
- 深入研究display:inline-block在浏览器中的应用
- iphone ios 编译时需要armv6架构
- Linux kernel 中对于Unicode的支持