一、线程安全
线程安全(Thread Safety)指的是在多线程环境中,程序的行为符合预期,不会出现数据不一致或状态不可预测的情况。反之,则是线程不安全。
线程安全问题主要来源于共享资源的并发访问,特别是当多个线程尝试修改同一资源时,如果没有适当的同步机制,就可能导致竞态条件(Race Condition)。
线程不安全的本质:模块内部有全局变量或静态变量。
举个例子:increment()函数就是线程不安全的,因为使用了全局变量。
#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 2
#define ITERATIONS 100000
// 全局变量
int counter = 0;
// 自增函数
void *increment(void *arg) {
for (int i = 0; i < ITERATIONS; i++) {
counter++;
}
pthread_exit(NULL);
}
int main() {
// 定义线程数组
pthread_t threads[NUM_THREADS];
// 创建两个线程
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, increment, NULL);
}
// 等待线程执行完成
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
// 打印最终结果
printf("Counter: %d\n", counter);
return 0;
}
在这个例子中,有两个线程并发地对一个全局变量 counter 进行自增操作。由于多个线程同时访问和修改同一个全局变量,可能会导致竞争条件(Race Condition)和数据不一致的问题。
运行该程序可能会得到意料之外的结果,因为两个线程同时对 counter 进行自增操作,而没有进行同步控制,导致两个线程对 counter 的修改相互覆盖,最终导致 counter 的值不符合预期。
为了解决这个问题,可以使用互斥锁(Mutex)来保护临界区(Critical Section),确保在同一时间只有一个线程可以访问和修改共享资源。
二、可重入函数
可重入函数是一种在多线程或并发环境下能够安全地被多个线程同时调用的函数。它们的设计避免了对全局状态的依赖,确保在函数执行期间不会出现数据混乱或竞争条件。
可重入函数的分类
-
显式可重入函数:
所有函数的参数都通过传值传递(没有使用指针)。
所有数据引用都是本地的自动栈变量,即没有引用静态或全局变量。
函数在任何调用情况下都可以被断言为可重入的。
-
隐式可重入函数:
函数中某些参数通过引用传递(使用了指针)。
当调用线程小心地传递指向线程私有数据的指针时,函数才可以被视为是可重入的。
可重入函数的特性:
可重入函数可以被一个或多个任务并发使用,而无需担心数据错误。
可重入函数可以在任何时刻被中断,稍后再继续运行,不会丢失数据。
可重入函数要么只使用本地变量,要么在使用全局变量时保护自己的数据。
- 不可重入函数
不可重入函数(Non-Reentrant Function)是指不能在多线程环境中安全调用的函数,因为它们可能会修改全局变量或静态变量,或者依赖于某些只初始化一次的资源。
不可重入函数的特点包括:
全局状态:使用全局变量或静态变量来存储状态。
唯一实例:依赖于程序中只存在一个实例的资源,如只初始化一次的文件句柄或通信端口。
顺序依赖:函数的执行依赖于特定的程序执行顺序。
当一个函数满足以下任意一个条件,则该函数是不可重入函数
函数内部使用了全局变量
函数内部使用了静态局部变量
函数返回值为全局变量或静态变量
函数内部使用了malloc或free函数
函数内部使用了标准 I/O 函数
函数内部调用了其它不可重入函数
标签:重入,函数,int,counter,嵌入式,线程,全局变量 From: https://www.cnblogs.com/o-O-oO/p/18653326原创 ZRQRS 知睿电子工程师