【问题标题】:c++ function pointer assignment is safe?c++函数指针赋值安全吗?
【发布时间】:2021-12-14 04:16:27
【问题描述】:

这段代码线程安全吗?

static PFN_SOMEFUNC pfnSomeFunc = nullptr;

PFN_SOMEFUNC SomeFuncGetter();

void CalledFromManyThreads() {
    if (pfnSomeFunc == nullptr)
        pfnSomeFunc = SomeFuncGetter();

    pfnSomeFunc();
}

这不是原子指针操作的问题。有一些特殊情况。

  • SomeFuncGetter() 总是返回相同的地址。
  • SomeFuncGetter() 是线程安全的。

【问题讨论】:

  • 不是。用std::atomic 包裹pfunSomeFunc。
  • 您可以将其设为静态 local 变量,并从 C++ 标准中的 this awesome quirk 中受益。
  • 代码中没有使用同步,因此您发布的代码中没有线程安全。在此代码之外会发生什么 - 这是您发布并提出问题的唯一代码。
  • 它不是线程安全的。 pfnSomeFunc = SomeFuncGetter(); = 本身不是线程安全的。也许,就在 30 年前,代码是“线程安全的”,因为处理器不那么复杂。但是在这个时代,比较和赋值(加上仅仅使用boolean 标志)不会让你获得线程安全。只有使用适当的同步才能为您提供这些结果。
  • @chaeyk 是的,有一个“同步问题”。您断言 pfSomeFunc 只能有一个值是不正确的 - 代码指定了两个值(nullptr 在初始化时,SomeFuncGetter() 在后续分配中返回的值)。由于此代码中没有同步,因此存在竞争条件会影响 pfSomeFunc 与 nullptr 的比较和分配 - 这些操作中的任何一个(或两个)都可能被另一个操作抢占(因为两个操作都不是原子的)部分完成时线程。

标签: c++ function-pointers


【解决方案1】:

它看起来不是线程安全的,因为全局变量可以被任何线程修改而无需同步。即使是赋值也不能保证是原子的。

您可以做的是利用一种语言特性,通过将静态变量移到函数中来保证线程安全的原子初始化(静态初始化惨败的一个非常常见的解决方案):

void CalledFromManyThreads() {
    static PFN_SOMEFUNC pfnSomeFunc = SomeFuncGetter();
    pfnSomeFunc();
}

现在它是线程安全的。如果你需要在多个地方缓存结果,你可以包装它:

PFN_SOMEFUNC GetSomeFunc()
{
    static PFN_SOMEFUNC pfnSomeFunc = SomeFuncGetter();
    return pfnSomeFunc;
}

void CalledFromManyThreads() {
    PFN_SOMEFUNC pfnSomeFunc = GetSomeFunc();
    pfnSomeFunc();
}

【讨论】:

  • 根据 C++11 标准,它确实是线程安全的。在 C++11 之前它不是线程安全的。
  • 在 Visual Studio 2010 中,一些线程从静态 pfnSomeFunc 获取空值。
  • VS 2010 不支持 C++11
  • @chaeyk -- 您需要使用 Visual Studio 2015、SP 3 或更高版本才能查看 paddy 向您展示的全部效果。 Visual Studio 的早期版本缺少此功能。 See this question and answer 关于“魔法静态”以及 Visual Studio 中对它们的支持。
  • @chaeyk -- 另外,如果你真的想使用 C++11,我建议不要使用 VS 2010 进行开发。编译器有几个 C++11 的“特性”实际上被破坏了(比如大括号初始化规则和 lambdas)——所以如果你使用任何 C++11 习惯用法来使用那个编译器,你就有风险。
猜你喜欢
  • 1970-01-01
  • 2013-01-23
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 2011-10-27
  • 1970-01-01
  • 2016-02-05
相关资源
最近更新 更多