std :: mutex是否顺序一致?

时间:2017-01-25 09:12:37

标签: c++ multithreading c++11 mutex memory-barriers

说,我有两个线程AB分别写入全局布尔变量fAfB,它们最初设置为false并且是受std::mutex对象mAmB分别保护:

// Thread A
mA.lock();
assert( fA == false );
fA = true;
mA.unlock();

// Thread B
mB.lock()
assert( fB == false );
fB = true;
mB.unlock()

是否可以在不同的帖子fAfB中以不同的顺序观察CD的修改?换句话说,可以使用以下程序

#include <atomic>
#include <cassert>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

mutex mA, mB, coutMutex;
bool fA = false, fB = false;

int main()
{
    thread A{ []{
            lock_guard<mutex> lock{mA};
            fA = true;
        } };
    thread B{ [] {
            lock_guard<mutex> lock{mB};
            fB = true;
        } };
    thread C{ [] { // reads fA, then fB
            mA.lock();
            const auto _1 = fA;
            mA.unlock();
            mB.lock();
            const auto _2 = fB;
            mB.unlock();
            lock_guard<mutex> lock{coutMutex};
            cout << "Thread C: fA = " << _1 << ", fB = " << _2 << endl;
        } };
    thread D{ [] { // reads fB, then fA (i. e. vice versa)
            mB.lock();
            const auto _3 = fB;
            mB.unlock();
            mA.lock();
            const auto _4 = fA;
            mA.unlock();
            lock_guard<mutex> lock{coutMutex};
            cout << "Thread D: fA = " << _4 << ", fB = " << _3 << endl;
        } };
    A.join(); B.join(); C.join(); D.join();
}

合法打印

Thread C: fA = 1, fB = 0
Thread D: fA = 0, fB = 1

根据C ++标准?

注意:可以使用std::atomic<bool>变量使用顺序一致的内存顺序或获取/释放内存顺序来实现自旋锁。所以问题是std::mutex是否表现为顺序一致的自旋锁或获取/释放内存顺序自旋锁。

2 个答案:

答案 0 :(得分:5)

是的,这是允许的,因此:不,std::mutex不一定是顺序一致的。

std::mutex未在标准中定义为顺序一致,仅为

  

30.4.1.2互斥体类型[thread.mutex.requirements.mutex]

     

11同步:对同一对象的事先解锁()操作应该   与(1.10)此操作同步 [lock()]

Synchronize-with 似乎与std::memory_order::release/acquire相同[见this question)。 据我所知,获取/释放螺旋锁将满足std :: mutex的标准。

答案 1 :(得分:0)

  

是否有可能观察到fA和fB的不同   订单在不同的线程C和D?

“获取”解锁的“释放”状态(和副作用历史记录)的锁的基本思想使之成为不可能:您承诺仅通过获取相应的锁来访问共享对象,并且该锁将“同步”。 ”,并通过执行解锁的线程查看所有过去的修改。因此,只能存在一个历史记录,不仅是关于解锁操作的历史记录,而且还存在访问共享对象的历史记录。