可能不是。我会指定它什么时候是可重入的,什么时候不是。但在此之前,您必须知道虽然每个处理器只有一组物理寄存器,但这些寄存器的状态是每个线程的。每个线程都保持自己的状态,不能摆弄其他线程的寄存器状态。操作系统确保了这一点。
一般来说,根据定义,编译器优化永远不会在正确的代码中引入错误。但是,优化可能会让现有的错误浮出水面,但无论是否使用优化,错误都存在于代码中。因此,无论您编写什么代码,无论优化如何,它都应该可以正常工作。这有例外,但它们与问题无关。
现在我来回答你的问题。假设该函数被调用了一些index和status1。考虑以下情况:
void func (uint8 index, uint8 status)
{
if (status == 1)
{
// Interrupt occurs here.
myArrayOfStructures[index].status = 1;
}
else if (status == 0)
{
myArrayOfStructures[index].status = 0;
}
else
{
/* Nothing */
}
}
index当中断发生并且同一个线程以相同的and为0调用同一个函数时status,它将将该索引处的数组元素的值设置为0。当第一次调用恢复时,它将1 写入同一个数组元素。我假设您认为这是不正确的行为,因为新状态丢失了。这表明该函数是不可重入的。
如果对数组元素的访问不是原子的,那么即使在单词的单线程含义中,该函数也不是可重入的,因为如果更新或读取数组元素,该线程可能会在中间中断。
现在让我们考虑两个线程同时执行具有相同索引但具有不同状态的函数。在这种情况下,会发生数据竞争。这意味着两件事。首先,结果是不确定的。您不知道将存储哪种状态。如果这与您的正确性要求一致,那很好。但很可能,您想存储最新状态,因此这种不确定性使其不可重入。其次,更大的问题是,单个数据竞争会使您的整个程序根据 C 标准具有未定义的行为。这当然意味着该功能不可重入。
在没有缓存一致性的对称多处理器系统中或在分布式内存计算机系统中,相同的数组元素可以有多个值,因此您将处于相同的情况,其中最近的状态是未知的。
编译器优化可以通过降低检测到错误的概率使函数看起来是可重入的。例如,如果编译器可以确定仅status使用 0 或 1 调用该函数,则它可以将生成的汇编代码优化为以下内容:
比较 status 和 myArrayOfStructures[index].status 是否相等。
如果不相等,则有条件地将状态写入 myArrayOfStructures[index].status。
这是有效的,因为全局变量被初始化为零,因此数组中的每个元素都将为 0 或 1。此代码对应于 x86 上仅有的两条指令,其中使函数不可重入的情况较少。
事实上,编译器优化甚至可以使函数可重入。例如,如果编译器可以确定该函数仅使用status0 调用,则该函数中的所有代码都将变为死代码,使其有效地可重入。这就是为什么我一开始就说“可能不会”。