【问题标题】:Is this use of std::atomic_thread_fence correct?这种使用 std::atomic_thread_fence 是否正确?
【发布时间】:2014-06-10 18:38:33
【问题描述】:

我想在构造函数中初始化一个字段,之后再也不更改它。我希望保证在构造函数完成后,每次读取该字段都会读取初始化值,无论读取发生在哪个线程中。

基本上,我想要的保证与 Java 中的 final 字段相同。

这是我尝试过的:

#include <atomic>
#include <iostream>
#include <thread>

struct Foo
{
    Foo(int x) : x(x)
    {
        // ensure all writes are visible to other threads
        std::atomic_thread_fence(std::memory_order_release);
    }

    int x;
};

void print_x(Foo const& foo)
{
    // I don't think I need an aquire fence here, because the object is
    // newly constructed, so there cannot be any stale reads.
    std::cout << foo.x << std::endl;
}

int main()
{
    Foo foo(1);
    std::thread t(print_x, foo);
    t.join();
}
  • 这是否保证始终打印1 或线程t 观察foo.x 处于未初始化状态?
  • 如果不使用成员初始化器 x(x) 而是使用显式赋值 this-&gt;x = x; 会怎样?
  • 如果x 不是int 而是某个类类型怎么办?
  • x 设为const int 是否会改变线程安全方面的任何内容?

【问题讨论】:

  • 巧合的是,您正在将foo 的副本传递给新生成的线程。如果您确实打算传递对 foo 的引用,则需要将其传递到引用包装器中,例如 std::thread t(print_x, std::cref(foo)); 或 - 对于非常量引用 - std::thread t(print_x, std::ref(foo));

标签: c++ multithreading


【解决方案1】:

基本上,如果其他一切都正确,就不应该有任何 问题。在初始化字段之后,在访问它之前 在任何线程中,您都需要某种内存同步; 这很清楚。否则其他线程怎么会知道它 被构造。如果在启动另一个之前初始化它 线程,然后创建线程将确保必要的 同步。 (这只适用于线程之间 创建和创建的线程。其他已经运行的线程 不同步。)之后,只要没有线程 修改值,不需要同步。

关于您的代码,您不需要围栏,因为 值在任何其他线程被初始化之前被初始化 创建,并创建线程确保必要的 同步。

【讨论】:

  • 那么,如果一个线程已经在运行,它可能会在构造函数完成后看到 x 未初始化?
  • 是的。在这种情况下,您需要在 两个 线程中进行同步。 (但是你如何通知正在运行的线程该对象现在存在。这可能会提供足够的同步,之后你就不需要任何额外的同步了。)
  • @TobiasBrandt 没有线程之间没有同步的“之前”或“之后”之类的东西。
  • @Casey 表达得很好。
  • @Tobias 基本上保证与 java 相同:如果其他线程看到 foo 实例,则保证看到正确的 x 值。但是你仍然需要某种同步来确保线程能看到新的 foo 实例。
猜你喜欢
  • 2018-06-27
  • 2014-08-07
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2018-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多