确保使用栅栏的其他线程的数据更新可见性



我有两个问题——考虑两个线程,一个对共享数据进行更改,另一个对该共享数据进行操作。在对数据执行任何操作之前,两个线程都会获取一个互斥对象。

我如何保证对数据进行操作的线程总是看到第一个线程所做的更改?是否需要一组获取/释放围栏,或者线程是否通过使用互斥来隐式同步?如果我不使用互斥(但以其他方式确保独占访问),该怎么办?

而且:如果没有交错/后续原子操作(比如,将标志存储在atomic_bool中,发出"就绪"信号或其他什么),fence真的能做什么吗?

这里有一个用例:

void func()
{
    std::atomic_bool quit = false;
    std::vector<float> data(100);
    std::mutex m;
    std::thread one([&]() 
        {
            while (!quit.load(std::memory_order_relaxed))
            {
                std::unique_lock<std::mutex> lock(m, std::try_to_lock);
                if (lock.owns_lock())
                {
                    for (int i = 0; i < data.size(); ++i)
                    {
                        data[i] = std::rand();
                    }
                    std::atomic_thread_fence(std::memory_order::memory_order_release);
                }
            }
        }
    );
    std::thread two([&]() 
        {
            while (!quit.load(std::memory_order_relaxed))
            {
                std::unique_lock<std::mutex> lock(m, std::try_to_lock);
                if (lock.owns_lock())
                {
                    // guaranteed that any changes from thread one to 'data' is seen after this fence?
                    std::atomic_thread_fence(std::memory_order::memory_order_acquire);
                    auto res = std::accumulate(data.begin(), data.end(), 0);
                    std::cout << "Accumulated result is: " << res << std::endl;
                }
            }
        }
    );
    fgetc(stdin);
    quit.store(true);
    one.join();  two.join();
}

我如何保证对数据进行操作的线程总是看到第一个线程所做的更改?是否需要一组获取/释放围栏,或者线程是否通过使用互斥来隐式同步?

Mutex做所有的工作。不需要额外的内存围栏。

如果我不使用互斥(但以其他方式确保独占访问),该怎么办?

这取决于确保exclusive access的机制。大多数机械装置不需要额外的围栏。[严格来说,独占访问定义意味着对应的内存排序。]

当访问实际上是并发时,可能需要内存围栏,但当访问其他变量时,您希望确保观察到一些不变量。

fence在没有交错/后续原子操作的情况下真的能做任何事情吗(比如,在atomic_bool中存储一个标志,发出"就绪"之类的信号)?

围栏不仅仅局限于原子操作。实际上,围栏只是屏障,它将所有访问(存储、加载或两者兼有,取决于围栏的类型)分为两组:围栏之前和之后。

Fence只是硬件使内部缓存与主内存同步的信号。实际上,它们是执行障碍,在通过这个障碍之前需要内存同步。因此得名。

最新更新