使用围栏

时间:2015-11-16 12:22:34

标签: multithreading c++11 synchronization memory-fences

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

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

并且:如果没有交错/后续原子操作(例如,在atomic_bool中存储标志,发出'ready'或者其他信号),围栏实际上会做什么吗?

这是一个用例:

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();
}

2 个答案:

答案 0 :(得分:1)

  

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

Mutex完成所有工作。不需要额外的内存栅栏。

  

如果我不使用互斥锁(但确保独占访问权限,该怎么办?)

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

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

  

如果没有交错/后续原子操作(例如,在atomic_bool中存储标志,发送信号并准备好等等),围栏实际上是做什么的吗?

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

答案 1 :(得分:0)

Fence仅仅是硬件使内部缓存与主内存同步的信号。实际上,它们是执行障碍 - 并且在传递此障碍之前需要内存同步。因此这个名字。