15

我知道在 C 中的函数内声明一个静态变量意味着该变量在函数调用之间保持其状态。在线程的上下文中,这会导致变量在多个线程上保持其状态,还是在每个线程之间具有单独的状态?

这是我正在努力回答的过去的纸质考试问题:

以下 C 函数旨在用于为其调用者分配唯一标识符 (UID):

get_uid() 
{
static int i = 0;
return i++;
}

解释 get_uid() 在被多个线程调用的环境中以何种方式可能无法正常工作。使用特定的示例场景,详细说明为什么以及如何可能会发生这种不正确的行为。

目前我假设每个线程都有一个单独的变量状态,但我不确定这是否正确,或者答案是否更多地与缺乏互斥有关。如果是这样的话,那么在这个例子中如何实现信号量呢?

4

6 回答 6

20

您的假设(线程有自己的副本)不正确。代码的主要问题是当多个线程调用该函数时get_uid(),可能存在 关于哪些线程递增并获取可能不唯一的 ID的竞争条件。i

于 2013-04-07T01:08:17.717 回答
13

一个进程的所有线程共享相同的地址空间。由于i是一个静态变量,它有一个固定的地址。它的“状态”只是该地址的内存内容,由所有线程共享。

后缀++运算符递增其参数并在递增之前产生参数的值。这些完成的顺序没有定义。一种可能的实现是

copy i to R1
copy R1 to R2
increment R2
copy R2 to i
return R1

如果有多个线程在运行,它们可以同时或穿插执行这些指令。为自己制定获得各种结果的序列。(请注意,每个线程都有自己的寄存器状态,即使对于在同一 CPU 上运行的线程也是如此,因为在切换线程时会保存和恢复寄存器。)

这种根据不同线程中操作的不确定顺序而产生不同结果的情况称为竞争条件,因为不同线程之间存在“竞争”,即哪个线程先执行哪个操作。

于 2013-04-07T01:18:36.860 回答
3

不,如果你想要一个值取决于使用它的线程的变量,你应该看看Thread Local Storage。

一个静态变量,你可以想象它真的像一个完全全局的变量。真的很相似。所以它由知道其地址的整个系统共享。

编辑:同样作为评论提醒它,如果您将此实现保留为静态变量,竞争条件可能会使该值i同时由多个线程递增,这意味着您不知道该值将由函数调用返回。在这种情况下,您应该通过所谓的同步对象(如互斥锁临界区)来保护访问。

于 2013-04-07T01:03:21.983 回答
1

由于这看起来像家庭作业,我将只回答其中的一部分,即每个线程将共享相同的i. IOW,线程没有自己的副本。我将把互斥位留给你。

于 2013-04-07T01:04:09.817 回答
1

每个线程将共享相同的静态变量,这很可能是一个全局变量。某些线程可能具有错误值的情况是竞争条件(增量不是在一次执行中完成,而是在 3 个汇编指令中完成,加载、增量、存储)。阅读此处,链接中的图表很好地解释了它。

比赛条件

于 2013-04-07T01:14:29.753 回答
1

如果您使用 gcc,则可以使用atomic 内置函数。我不确定其他编译器有什么可用的。

int get_uid() 
{
    static int i = 0;
    return __atomic_fetch_add(&i, 1, __ATOMIC_SEQ_CST);
}

这将确保变量一次不能被多个线程操作。

于 2014-08-06T21:10:07.753 回答