如何解决用于维护 C++ 中静态局部变量的服务数据的线程安全性?
考虑以下场景。我们有一个带有静态局部变量的 C++ 函数:
void function()
{
static int variable = obtain();
//blahblablah
}
该函数需要同时从多个线程调用,因此我们添加一个临界区以避免并发访问静态局部变量:
void functionThreadSafe()
{
CriticalSectionLockClass lock( criticalSection );
static int variable = obtain();
//blahblablah
}
但这足够了吗?我的意思是有一些魔法可以使变量初始化不超过一次。因此,运行时维护了一些服务数据,指示每个静态本地是否已初始化。
上述代码中的关键部分也会保护该服务数据吗?这种情况下需要额外的保护吗?
Consider the following scenario. We have a C++ function with a static local variable:
void function()
{
static int variable = obtain();
//blahblablah
}
the function needs to be called from multiple threads concurrently, so we add a critical section to avoid concurrent access to the static local:
void functionThreadSafe()
{
CriticalSectionLockClass lock( criticalSection );
static int variable = obtain();
//blahblablah
}
but will this be enough? I mean there's some magic that makes the variable being initialized no more than once. So there's some service data maintained by the runtime that indicates whether each static local has already been initialized.
Will the critical section in the above code protect that service data as well? Is any extra protection required for this scenario?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
C++ 规定静态变量只能初始化一次——但是 C++ 还不处理线程。
gcc(至少在 *nix 系统上)发挥了适当的作用来安全地保护初始化此类静态变量的多个线程。根据 此链接,msvc 没有 - 在这种情况下,您必须自己锁定初始化。
用关键部分保护初始化应该保护所有这些 - 即你的 functionThreadSafe() 没问题 - (除非
obtain()
本身调用functionThreadSafe()
这篇博客文章在这方面值得一读。
就我个人而言,为了避免意外,我会尝试重写此代码,以便您可以在创建任何线程之前自行初始化
variable
一次 - 例如C++ says that your static variable should only be initialized once - however C++ doesn't deal with threads(yet).
gcc(atleast on *nix systems) does the proper magic to safely guard multiple threads initializing such a static variable. According to this link, msvc does not - and in such a case you'll have to lock the initialization yourself.
Guarding the initialization with a critical section should protect all this - i.e. your functionThreadSafe() is ok - (unless
obtain()
itself callsfunctionThreadSafe()
This blog article is worth a read in this regard.
Personally, to avoid surprises I'd try to rewrite this so you can initialize
variable
yourself, once, before you create any threads - e.g.(我已经在另一个问题上发布了这个问题,但这也是这个问题的答案)
这是我的看法(如果你真的无法在线程启动之前初始化它):
我已经看到(并使用过)类似这样的东西使用 boost::once 保护静态初始化
根据我的理解,这样所有线程都会等待 boost::call_once,除了创建静态变量的线程之外。它只会被创建一次,然后就不会再被调用。然后你就不再有锁了。
(I have post this on another question, but it is also an answer to this one)
Here is my take (if really you can't initialize it before threads are launched):
I've seen (and used) something like this to protect static initialization, using boost::once
In my understanding, this way all threads wait on boost::call_once except one that will create the static variable. It will be created only once and then will never be called again. And then you have no lock any more.
为了避免在任何情况下锁定,您可以这样做:
To avoid the locking in any case, you can go with this:
您可以尝试一些横向回避,这可能会解决您的根本问题:
int 变量
设为线程局部静态这个变量的值或者通过它互相传递数据。int
,您可以使用原子读/写,例如InterlockedCompareExchange()
或其平台上的等效项。这允许多个线程安全地访问变量而无需锁定。不过,它仅适用于硬件本机原子类型(例如,32 位和 64 位字)。您还必须弄清楚如果两个线程想要同时写入该变量该怎么办(即,一个线程在执行比较和交换操作时会发现另一个线程已写入该变量)。Some lateral dodges you can try that might solve your underlying problem:
int variable
be a thread-local static, if the different threads don't actually need to share the value of this variable or pass data to each other through it.int
on x86, you can use an atomic read/write such as byInterlockedCompareExchange()
or its equivalent on your platform. This lets multiple threads safely access the variable without locks. It only works for hardware-native atomic types, though (eg, 32-bit and 64-bit words). You will also have to figure out what to do if two threads want to write to the variable at the same time (ie, one will discover that the other has written to it when it performs the compare-and-swap op).