C++ 线程安全和可重入函数

线程安全

线程安全是指在多线程环境下,同一函数或函数库被不同线程调用,不会出现数据不一致的情况。

如何确保一个函数是线程安全的:

1.对共享资源加锁。

2.从逻辑上进行设计,保证资源的访问修改不会冲突。

一般情况下我们使用加锁的方式保证线程安全,具体加锁操作有互斥锁、条件变量、信号量以及读写锁。

 

可重入函数
若一个程序或子程序(或函数)可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant)的。即当该子程序正在被执行是,执行线程可以再次进入并执行它,仍然获取符合预期的结果。

上面提到的线程安全是多线程并发执行时,而可重入强调对单个线程执行时重新进入同一个子程序仍然是安全的。

若一个函数式可重入的,应满足下述条件:

不能含有静态或全局非常量数据。
不能返回静态或全局非常量数据的地址
不能调用标准 I/O
调用的函数也必须是可重入的
没有动态分配或释放堆资源
即保证可重入函数使用的所有变量都保存在调用栈的当前栈帧中,保证执行该函数时使用的栈帧不重入、不相互覆盖,从而保证可重入执行安全。

线程安全与可重入函数的对比
可重入函数未必是线程安全的;线程安全函数未必是可重入的。

例如,一个函数打开某个文件并读入数据。这个函数是可重入的,因为它的多个实例同时执行不会造成冲突;但它不是线程安全的,因为在它读入文件时可能有别的线程正在修改该文件,为了线程安全必须对文件加“同步锁”。
另一个例子,函数在它的函数体内部访问共享资源使用了加锁、解锁操作,所以它是线程安全的,但是却不可重入。因为若该函数一个实例运行到已经执行加锁但未执行解锁时被停下来,系统又启动该函数的另外一个实例,则新的实例在加锁处将转入等待。如果该函数是一个中断处理服务,在中断处理时又发生新的中断将导致资源死锁。fprintf函数就是线程安全但不可重入。

判断一个函数是不是可重入函数,关键在与,是否能能够在被中断后,处理完中断后恢复运行得到正确的结果。在Linux中可以指执行完信号处理函数结果正确。

如果函数对异步信号处理程序的重入是安全的,那么就可以说函数是”异步-信号安全”的。

可重入函数与是否使用锁无关,而线程安全大多需要锁机制来保证线程安全。

如果程序在获得锁之后被中断,转去执行信号处理函数,而恰好该信号处理函数也调用了该函数,此时锁已经被占用,线程处理函数无法获得锁,可能导致死锁。

 

原文链接:https://blog.csdn.net/a1414345/article/details/71794597

posted on 2023-05-23 18:24  林西索  阅读(333)  评论(0编辑  收藏  举报