什么是可重入程序?
目录
可重入程序(Reentrant Program)的定义
可重入程序是指可以被多个线程或任务同时调用而不会导致数据错误或逻辑混乱的程序(如函数、子程序或代码模块)。它的核心特点是:在任意时刻被中断后,再次进入时仍能正确执行。
关键特性
-
不使用静态/全局的非恒定数据
- 可重入程序的执行不依赖全局变量或静态变量(除非是只读的常量),所有数据通过参数或局部变量传递。
- 避免多个调用者共享同一份可变数据。
-
不调用不可重入的函数
- 如果函数内部调用了其他不可重入的函数(如使用了静态缓存的
strtok),则自身也不可重入。
- 如果函数内部调用了其他不可重入的函数(如使用了静态缓存的
-
不依赖外部状态
- 例如,不直接读取硬件状态或外部设备的实时变化数据(除非通过参数传递)。
-
可被中断后安全恢复
- 在中断服务程序(ISR)或多线程环境中,即使执行被中断,再次进入时仍能正常工作。
为什么需要可重入程序?
- 多线程/多任务环境:多个线程可能同时调用同一函数。
- 中断处理:主程序执行时可能被中断,中断服务程序可能调用相同函数。
- 递归调用:函数可能间接调用自身。
如果程序不可重入,会导致数据竞争、结果错误或系统崩溃。
可重入 vs. 线程安全
| 特性 | 可重入程序(Reentrant) | 线程安全程序(Thread-Safe) |
|---|---|---|
| 核心要求 | 可被同时调用且结果正确 | 多线程调用时通过同步机制保证安全 |
| 实现方式 | 避免共享数据,仅用局部变量 | 可能用锁、原子操作保护共享数据 |
| 性能 | 通常更高(无锁) | 可能因同步开销降低性能 |
| 典型场景 | 中断处理、信号处理函数 | 多线程共享资源的函数 |
- 可重入一定是线程安全的,但线程安全不一定是可重入的(例如用互斥锁保护的函数是可重入的,但依赖锁的不可重入函数不是)。
示例分析
1. 不可重入函数
// 不可重入:使用了静态变量
int counter() {
static int count = 0; // 静态变量在多次调用间共享
return ++count;
}
- 问题:多个线程同时调用
counter()会导致count的更新竞争,结果不可预测。
2. 可重入函数
// 可重入:仅依赖参数和局部变量
int add(int a, int b) {
return a + b;
}
- 安全:无论多少线程同时调用
add,结果始终正确。
3. 线程安全但不可重入的函数
#include <pthread.h>
pthread_mutex_t lock;
// 线程安全(通过锁),但不可重入(锁未处理递归调用)
void safe_but_not_reentrant() {
pthread_mutex_lock(&lock);
// 临界区操作...
pthread_mutex_unlock(&lock);
}
- 问题:若同一线程递归调用此函数,会导致死锁(锁不可重入)。
如何编写可重入程序?
- 避免全局/静态变量:改用局部变量或通过参数传递数据。
- 使用常量数据:全局数据应为
const(如const char*)。 - 不调用不可重入函数:如避免
malloc、printf等(某些实现不可重入)。 - 谨慎处理中断:确保中断服务程序调用的函数是可重入的。
典型应用场景
- 操作系统内核:系统调用、中断处理函数必须是可重入的。
- 嵌入式系统:实时任务可能随时被中断。
- 信号处理函数:POSIX要求信号处理函数必须可重入。
总结
- 可重入程序的核心是无状态或状态隔离,不依赖共享的可变数据。
- 在多线程、中断、递归等场景下,可重入性是保证正确性的关键。
- 现代编程中,尽量将函数设计为可重入的(如纯函数),可大幅降低并发风险。
Do not communicate by sharing memory; instead, share memory by communicating.

浙公网安备 33010602011771号