可重入若一個程序或子程序可以「在任意時刻被中斷然後作業系統調度執行另一段程式碼,這段程式碼又使用了該副程式不會出錯」,則稱其為可重入(reentrant 或 re-entrant)的。即當該副程式正在運作時,執行线程可以再次進入並執行它,仍然可得到符合設計時所預期的結果。與多執行緒併發執行的线程安全不同,可重入強調對單一執行緒執行時重新進入同一個子程序仍然是安全的。 可重入概念是在單執行緒作業系統的時代提出的。一個子程序的重入,可能由於自身原因,如執行了jmp或者call,類似于子程序的遞迴调用;或者由於作業系统的中斷回應。UNIX系统的signal的处理,即子程序被中断处理程序或者signal处理程序调用。所以,可重入也可称作“异步信号安全”。这里的异步是指信号中断可发生在任意时刻。 重入的子程序,按照后进先出线性序依次执行。 若一个函数是可重入的,則该函数應當满足下述条件:
上述条件就是要求可重入函数使用的所有变量都保存在呼叫堆疊的目前函数栈(frame)上,因此同一执行线程重入执行该函数时加载了新的函数帧,与前一次执行该函数时使用的函数帧不冲突、不互相覆盖,从而保证了可重入执行安全。 多「用户/对象/进程优先级」以及多元處理(Multiple processes),一般会使得对可重入代码的控制变得复杂。同时,IO代码通常不是可重入的,因为他们依赖于像磁盘这样共享的、单独的(類似編程中的靜態、全域)资源。 可重入性是函数式编程的關鍵特性之一。 例子在以下的C语言代码中,函数 int g_var = 1;
int f()
{
g_var = g_var + 2;
return g_var;
}
int g()
{
return f() + 2;
}
以上代码中, 稍作修改后,两个函数都是可重入的: int f(int i)
{
return i + 2;
}
int g(int i)
{
return f(i) + 2;
}
与线程安全的关系可重入与线程安全两个概念都关系到函数处理资源的方式。但是,他们有重大区别
下述例子,是线程安全的,但不是可重入的。 int function()
{
mutex_lock();
...
function body
...
mutex_unlock();
}
多线程执行时,获得了互斥锁的线程总能获得CPU时间片,向前推进执行进度,最终解开互斥锁,使得别的线程也能获得互斥锁进入临界区。但是,如果在单线程背景下第一次执行该函数时已经获得互斥锁进入临界区,这时该函数被重入执行,这将在重新申请互斥锁时被饿死(starvation),因为获得了互斥锁的该函数的第一次执行将永远没有机会再获得CPU时间片。 参见外部链接
|