函数的可重入与线程安全的关系

在多任务环境中,函数的可重入性和线程安全性至关重要。可重入函数允许并发调用而不会导致结果错误,而不可重入函数则可能导致数据破坏。静态变量、全局变量、malloc/free以及标准I/O函数的使用会使函数变为不可重入。为确保线程安全,可以将资源保护起来,通过临界区实现互斥访问。在无法避免使用不可重入元素时,可以利用互斥量、信号量等同步机制来保护临界资源,确保函数在多任务环境中的正确执行。
摘要由CSDN通过智能技术生成

函数的可重入与线程安全的关系
原创: 王利涛 宅学部落 1周前
在嵌入式裸机时代,也就是无OS时代,我们在裸机环境下编写C语言程序非常简单,实现一个函数,然后将函数接口API提供给其它模块
调用就可以了。比如下面的函数,我们实现一个sum函数,用来求两个数的和:
但是在一个运行OS的多任务环境中,我们在编写sum函数时就要注意一些细节了:我们编写的sum函数可能会被多个任务调用,而且可
能在sum函数执行的过程被打断,接着在另一个任务中再次调用sum函数。而在上面的sum函数实现中,我们定义了一个静态变量sum用
来保存两个数相加的临时结果,静态变量是保存到数据段中的,大家可以想一想,在一个任务A中正在执行sum(1,2)函数中的第4行,此时
任务被打断挂起,接着运行任务B,在任务B中接着执行sum(10,20)函数,执行结束后接着运行任务A,A获得CPU控制权后继续运行
sum(1,2)的第5行,此时sum(1,2)的返回结果就变成了30,而不是正确结果3。
在一个多任务环境中,如果一个函数可以重复并发调用,而且多次调用并不会影响函数的运行结果,那么这个函数是可重入的,我们称这
个函数为:可重入函数。在上面的sum函数实现中,当其被多次并发调用时,函数的运行结果并不确定,我们称其为不可重入函数。
我们如何去判定一个函数是可重入的,还是不可重入的呢?很简单,当一个函数满足下面任一条件,那么这个函数就是不可重入函数。
函数内部使用了全局变量
函数内部使用了静态局部变量
函数返回值为全局变量或静态变量
函数内部使用了malloc/free函数
函数北部使用了标准I/O函数
函数内部调用其它不可重入函数
不可重入函数在一个多任务环境中不能被多次并发调用,如果一个函数可能被多次调用,那么我们设计这个函数时尽量要将其设计为可重
入函数。
不使用/返回静态变量、全局变量
不使用标准I/O函数
不使用malloc/free函数
不调用不可重入函数
在函数设计时,只要注意上面的原则,那么我们就可以将一个函数设计为可重入函数,可重入函数在多任务环境下可以被多次并发调用,
是线程安全的,程序员可以放心大胆地调用。
理想很丰满,现实很骨干。我们在编程中如果说不用malloc/free、全局变量,那是不现实的。只要我们使用了这些全局变量,静态变量,
那么函数就变成不可重入了,在多任务环境下使用这个函数就变得线程不安全了,那怎么办呢?
方法还是有的,一个函数之所以变得不可重入,就是因为函数内有一些资源是全局共享的,在多任务环境下多次并发调用该函数时可能会
破坏掉这些共享的全局资源。我们如果把这些资源在访问的时候保护起来,不让其它任务访问(即互斥访问),即同一时刻只允许一个进程
访问就安全了。这些被保护的资源我们称为临界资源,访问这些临界资源的代码段,我们称之为临界区。临界区的访问方式为互斥访问,
即同一时刻只允许一个进程访问。
临界区的实现方式有很多种,不同的操作系统可能会提供不同的实现方式。我们可以通过下面的操作原语来实现一个临界区:
EnterCriticalSection()
LeaveCriticalSection()
不同的操作系统,具体的实现手段可能不一样,常见的方法有:关中断;实现互斥访问,比如通过信号量、互斥量、自旋锁等实现,甚至
原子操作等。比如在uc/os操作系统中,我们使用关中断的方式来实现临界区,确保函数的线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值