【问题标题】:Thread safety of static initialized unsafe call静态初始化不安全调用的线程安全
【发布时间】:2016-12-23 03:10:07
【问题描述】:

在图书馆的某个地方,我有一个看起来像的函数 -

inline int getIword()
{
    static int i = std::ios_base::xalloc();
    return i;
}

现在您可以阅读有关std::ios_base::xalloc() 调用here 的信息,但我想从提及的链接中强调这一行 -

这个函数是线程安全的;多个线程的并发访问不会导致数据竞争。 (C++14 起)

上面写着“自 C++14 起”,但我也需要 C++11 支持。由于函数调用实际上是在初始化 getIword() 方法的静态局部变量,并且我们知道 local static variable initialization is thread safe 用于 C++11 代码,因此假设此代码是安全的 -

  • 如果仅对函数进行后续读取调用,则安全,例如。 auto something = getIword().

  • 安全吗?如果代码如下所示:

...

operator<<(std::ostream &os, T const value)
{
    if (value == ...) {
        os.iword(getIword()) = 1;
    } else if (value == ...) {
        os.iword(getIword()) = 0;
    }
    return os;
}

...

如果在后面的示例中不安全,我应该将 lock_guards 放在哪里以使其对 C++11 安全?围绕return i 或整个方法或在哪里进行调用?

【问题讨论】:

  • 检查您的 C++ 实现是否已经是线程安全的可能是值得的。 保证在 C++14 中是新的,但对于实际实现来说很常见,可以尽早提供这些细节。
  • @MSalters 关于线程安全静态初始化的保证可以追溯到 C++11。
  • @NathanOliver MSalters 可能在谈论该功能的保证。

标签: c++ multithreading c++11 static thread-safety


【解决方案1】:

静态局部变量初始化是线程安全的,因为如果多个线程调用该函数,那么只有其中一个线程会实际初始化变量。它不保护你初始化的东西。这意味着在这种情况下,如果您有两个不同的线程,一个调用 getIword,另一个调用另一个恰好同时调用 std::ios_base::xalloc() 的函数,那么这两个调用将不会同步,您将拥有一个数据比赛反过来又是未定义的行为。

【讨论】:

    【解决方案2】:

    在我的电脑上,我可以看到std::ios_base::xalloc() 的实现是:

        static int __CLRCALL_OR_CDECL xalloc()
        {   // allocate new iword/pword index
        _BEGIN_LOCK(_LOCK_STREAM)   // lock thread to ensure atomicity
            return (_Index++);
        _END_LOCK()
        }
    

    所以基本上它只是做一个增量值,没有线程安全保证你会在竞争条件下从std::ios_base::xalloc() 中得到一些重复值。

    我会采取的解决方案是使用std::atomic 编写您自己的xalloc()

    int xalloc()
    {
        static std::atomic_int i;
        return i++;
    }
    

    或者将std::ios_base::xalloc() 包装成一个带锁的新方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-22
      • 2010-12-31
      • 2010-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      相关资源
      最近更新 更多