如何解决用于维护 C++ 中静态局部变量的服务数据的线程安全性?

发布于 2024-09-03 15:56:27 字数 464 浏览 1 评论 0原文

考虑以下场景。我们有一个带有静态局部变量的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

薄荷→糖丶微凉 2024-09-10 15:56:27

C++ 规定静态变量只能初始化一次——但是 C++ 还不处理线程。

gcc(至少在 *nix 系统上)发挥了适当的作用来安全地保护初始化此类静态变量的多个线程。根据 此链接,msvc 没有 - 在这种情况下,您必须自己锁定初始化。

用关键部分保护初始化应该保护所有这些 - 即你的 functionThreadSafe() 没问题 - (除非 obtain() 本身调用 functionThreadSafe()

这篇博客文章在这方面值得一读。

就我个人而言,为了避免意外,我会尝试重写此代码,以便您可以在创建任何线程之前自行初始化 variable 一次 - 例如

static int variable = 0;
void init_variable() //call this once, at the start of main()
{
  variable = obtain();
}

void function() 
{
  //use variable, lock if you write to it
}

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 calls functionThreadSafe()

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.

static int variable = 0;
void init_variable() //call this once, at the start of main()
{
  variable = obtain();
}

void function() 
{
  //use variable, lock if you write to it
}
孤独岁月 2024-09-10 15:56:27

(我已经在另一个问题上发布了这个问题,但这也是这个问题的答案)

这是我的看法(如果你真的无法在线程启动之前初始化它):

我已经看到(并使用过)类似这样的东西使用 boost::once 保护静态初始化

#include <boost/thread/once.hpp>

boost::once_flag flag;

// get thingy
const Thingy & get()
{
    static Thingy thingy;

    return thingy;
}

// create function
void create()
{
     get();
}

void use()
{
    // Ensure only one thread get to create first before all other
    boost::call_once( &create, flag );

    // get a constructed thingy
    const Thingy & thingy = get(); 

    // use it
    thingy.etc..()          
}

根据我的理解,这样所有线程都会等待 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

#include <boost/thread/once.hpp>

boost::once_flag flag;

// get thingy
const Thingy & get()
{
    static Thingy thingy;

    return thingy;
}

// create function
void create()
{
     get();
}

void use()
{
    // Ensure only one thread get to create first before all other
    boost::call_once( &create, flag );

    // get a constructed thingy
    const Thingy & thingy = get(); 

    // use it
    thingy.etc..()          
}

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.

画中仙 2024-09-10 15:56:27

为了避免在任何情况下锁定,您可以这样做:

void functionThreadSafe()
{
    static int *variable = 0;
    if (variable == 0)
    {
       CriticalSectionLockClass lock( criticalSection );
       // Double check for initialization in different thread
       if (variable == 0)
       {
          variable = new int(obtain());
       }
    }
    //blahblablah
}

To avoid the locking in any case, you can go with this:

void functionThreadSafe()
{
    static int *variable = 0;
    if (variable == 0)
    {
       CriticalSectionLockClass lock( criticalSection );
       // Double check for initialization in different thread
       if (variable == 0)
       {
          variable = new int(obtain());
       }
    }
    //blahblablah
}
柒七 2024-09-10 15:56:27

您可以尝试一些横向回避,这可能会解决您的根本问题:

  • 如果不同的线程实际上不需要共享,您可以将 int 变量 设为线程局部静态这个变量的值或者通过它互相传递数据。
  • 对于 x86 上的 int ,您可以使用原子读/写,例如 InterlockedCompareExchange() 或其平台上的等效项。这允许多个线程安全地访问变量而无需锁定。不过,它仅适用于硬件本机原子类型(例如,32 位和 64 位字)。您还必须弄清楚如果两个线程想要同时写入该变量该怎么办(即,一个线程在执行比较和交换操作时会发现另一个线程已写入该变量)。

Some lateral dodges you can try that might solve your underlying problem:

  • you could make 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.
  • for an int on x86, you can use an atomic read/write such as by InterlockedCompareExchange() 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).
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文