C全局静态变量 - 是否在线程之间共享?

8
在C语言中,将变量在全局范围内声明为静态变量会使其成为全局变量。这个全局变量是在线程之间共享的还是每个线程都有自己的分配?如果它们在线程之间共享,那么如何轻松地使预先存在的库中的全局变量对于每个线程都是唯一的/非共享的呢?基本上,我需要以线程安全的方式使用一个预先存在的带有全局变量的C库。

4
将变量声明为static会使其成为文件作用域变量。将它声明为非static则会使它成为全局变量。 - William Pursell
你使用什么操作系统?我认为可能有特定于平台的解决方案适用于Update2。 - Kirill V. Lyadvinsky
在Windows上进行开发/调试。在Linux上部署。 - jameszhao00
@WilliamPursell 我不会说“声明为非静态”,我会说“不声明为静态”。 - SO Stinks
3个回答

20
它对整个进程可见,即所有线程都可以访问。当然,这只是实践中的情况。从理论上讲,你无法确定,因为线程与C标准无关(至少在c99中无关,这是当时提出这个问题时生效的标准)。
但我使用过的所有线程库都会将全局变量暴露给所有线程。

更新1:

许多线程库(例如pthread)允许您创建特定于线程的数据,这是一种函数创建和使用特定于线程的数据而无需将其通过函数传递的方法。

例如,一个返回伪随机数的函数可能希望每个线程都有一个独立的种子。因此,每次调用函数时,它都会创建或附加到保持该种子的线程特定块(使用某种键)。

这允许函数保持与非线程化函数相同的签名(如果它们是ISO C函数,则很重要),因为另一种解决方案涉及向函数调用本身添加特定于线程的指针。

另一个可能性是拥有一个全局变量数组,每个线程获取其中一个,例如:

int fDone[10];
int idx;
: : :
for (i = 0; i < 10; i++) {
    idx = i;
    startThread (function, i);
    while (idx >= 0)
        yield();
}

void function () {
    int myIdx = idx;
    idx = -1;
    while (1) {
        : : :
    }
}
这将允许线程函数知道它所使用的数组中的哪个全局变量属于它。
毫无疑问还有其他方法,但如果不了解目标环境,讨论它们没有太多意义。
更新2:
在多线程环境中使用非线程安全库最简单的方法是提供带互斥保护的包装调用。
例如,假设您的库有一个非线程安全的 doThis() 函数。您需要为其提供一个包装器:
void myDoThis (a, b) {
    static mutex_t serialize;
    mutex_claim (&serialize);
    doThis (a, b);
    mutex_release (&serialize);
}

在那里发生的事情是,一次只能有一个线程获取到互斥锁(从而调用非线程安全函数)。其他线程将被阻塞,直到当前线程返回。


没关系,詹姆斯,我现在还跟得上 :-) - paxdiablo
互斥锁并不总是可行的,因为库可能会在一个函数的多次调用之间在全局变量中存储临时数据。例如,计算某个变量的方差。 - Kirill V. Lyadvinsky
这是个好观点,@Kirill,但你可以将序列化互斥锁提升到执行层次结构中,例如,startDoThis(),myDoThis(),myDoThis(),...,stopDoThis(),在startDoThis()中声明互斥锁,在stopDoThis()中释放。但是,此时我可能会去寻找更好的库,或者自己编写 :-) - paxdiablo
如果您可以访问库源代码,最好编写包装类并将所有全局变量移入其中。如果您无法访问源代码,则无法确定在执行层次结构的哪个级别上可以使用互斥锁。 - Kirill V. Lyadvinsky
我有以下跟进问题:
  1. 静态变量是否存储在栈本身中类似于全局变量?如果是这样,它们是如何受保护以允许仅限本地类访问的?
  2. 在多线程上下文中,担心这种内存可能会被其他线程/内核直接访问吗?或者为什么不能在多进程/线程环境中使用静态/全局变量?
- Swapna
@Swapna,这可能更好地作为一个新问题:(1)不,线程通常有自己的堆栈。(2)不,问题不在于内存的使用,而在于A线程可能会在B线程更新的一半尝试使用它(即,在其处于不一致状态时)。 - paxdiablo

1

C/C++标准不支持线程。因此,所有在线程之间共享的变量。 线程支持是在C/C++运行时库中实现的,该库不是标准的一部分。运行时库对于每个C/C++实现都是特定的。如果您想在C++中编写可移植代码,可以使用boost interprocess library

要在Microsoft Visual Studio中声明线程本地变量,可以使用Microsoft特定的关键字__declspec( thread )


1

正如@Pax所提到的,静态变量对所有线程都是可见的。没有与特定线程相关联的C++数据结构。

然而,在Windows上,您可以使用TlsAlloc API为线程特定数据分配索引,并将该索引放入静态变量中。每个线程都有自己的插槽,您可以使用此索引和TlsGetValue和TlsSetValue访问它。有关更多信息,请阅读MSDN上的使用线程本地存储

更新:无法使现有库中的全局变量具有线程特定性。任何解决方案都需要您修改代码以知道数据具有线程亲和性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接