Dee*_*ain 5 multithreading operating-system
我知道这个论坛已经讨论了很多。但有一件事仍然让我感到困惑。维基百科提到每个可重入代码都是线程安全的。 http://en.wikipedia.org/wiki/Reentrant_%28subroutine%29 后来给出了一个可重入但不是线程安全的函数的例子。
int t;
void swap(int *x, int *y)
{
int s;
s = t; // save global variable
t = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = t;
t = s; // restore global variable
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
Run Code Online (Sandbox Code Playgroud)
这让我很困惑。所有重入代码都是线程安全的吗?此外,所有递归函数都是线程安全的。我无法想象差异。
谢谢
可重入和线程安全的概念是相关的,但并不等同。您可以编写一个非线程安全的可重入函数,以及一个不可重入的线程安全函数。我将使用 C# 作为示例:
非线程安全的可重入函数
该函数反转数组的条目:
void Reverse(int[] data) {
if (data == null || data.Length < 2) return;
for (var i = 0 ; i != data.Length/2 ; i++) {
int tmp = data[i];
data[i] = data[data.Length-i-1];
data[data.Length-i-1] = tmp;
}
}
Run Code Online (Sandbox Code Playgroud)
这个函数显然是可重入的,因为它不引用外部资源。然而,它的线程安全性是以不从多个线程传递相同的内容为条件的data。如果多个线程同时传递Reverse数组的同一个实例,则可能会产生不正确的结果。使该函数无条件线程安全的一种方法是添加锁:
void Reverse(int[] data) {
if (data == null || data.Length < 2) return;
lock (data) {
for (var i = 0 ; i != data.Length/2 ; i++) {
int tmp = data[i];
data[i] = data[data.Length-i-1];
data[data.Length-i-1] = tmp;
}
}
}
Run Code Online (Sandbox Code Playgroud)
不可重入的线程安全函数
该函数调用函数f() c次数,并返回比其他值返回次数多的值。
static int[] counts = new int[65536];
unsigned short MaxCount(Func<unsigned short> f, int c) {
lock(counts) {
Array.Clear(counts, 0, counts.Length);
for (var i = 0 ; i != c ; i++) {
counts[f()]++;
}
unsigned short res = 0;
for (var i = 1 ; i != counts.Length ; i++) {
if (counts[i] > counts[res]) {
res = i;
}
}
return res;
}
}
Run Code Online (Sandbox Code Playgroud)
该函数是线程安全的,因为它锁定用于计数的静态数组。但是,它是不可重入的:例如,如果f传入的函子是 call MaxCount,则会返回错误的结果。
该函数不是线程安全的,因为它访问t函数范围之外的资源(未在堆栈上分配),没有任何保护机制(例如锁)来确保访问t是原子的。
维基百科页面实际上指出:
可重入子例程可以实现线程安全,但仅此条件可能不足以满足所有情况。
(我的重点)。
更新:
可重入(如本文中所定义)只是意味着单个执行线程(想想 DOS)可以从执行例程的中间“剥离指令指针”,将其放在其他地方,并继续通过新代码的线性流程(例如,DOS 时代的中断子例程),并且相同的线性流程可以第二次返回到该函数。在这种有限的情况下,函数的第二次调用必须在控制权从中断子例程转移回“常规”程序执行之前完成,“常规”程序执行将在例程中指令指针最初被删除的同一点处恢复。此方案不允许任意线程调度,而是允许将控制权切换到新点的中断,然后该中断完成并恢复到中断点。
请注意,在现实生活中,事情可能没那么简单。我不再完全确定了(已经……20年了?),但我认为一个中断例程可以中断并且已经在进行中的中断例程(例如软调试器中断可以中断定时器中断等)。