通过检查条件并重新检查来获取锁定

时间:2018-05-29 06:36:18

标签: c++ multithreading memory-barriers

这样的事情有效:

std::vector<std::vector<int>> data;
std::shared_mutex m;
...

void Resize() {
    // AreAllVectorsEmpty: std::all_of(data.begin(), data.end(), [](auto& v) { return v.empty(); }
    if (!AreAllVectorsEmpty()) {
        m.lock();
        if (!AreAllVectorsEmpty()) {
            data.resize(new_size);
        }
        m.unlock();
    }
}

我正在检查AreAllVectosEmpty(),然后如果条件成功,则进行锁定,然后再次检查相同的条件是否进行调整大小。

这是线程安全吗? Resize仅由一个线程调用,但其他线程操纵data的元素。

是否要求AreAllVectorsEmpty具有内存栅栏或获取语义?

编辑:当m.lock获得Resize时,其他线程将会阻止。

编辑:我们还假设new_size足够大以至于重新分配。

编辑:更新shared_mutex的代码。

编辑:AreAllVectorsEmtpy正在迭代数据向量。没有其他人修改数据向量,但数据[0],数据[1]等由其他线程修改。我的假设是因为data [0]的size变量在vector中并且是一个简单的整数,所以访问data [0] .size(),data [1] .size()等...是安全的。 Resize主题。 AreAllVectorsEmpty正在迭代data并检查vector.empty()

4 个答案:

答案 0 :(得分:3)

我会使用shared_mutex并使用:

  • 只读取向量的所有线程中的共享锁(在读取向量时)
  • 调整矢量大小时此线程中的唯一锁定

我认为首先检查大小,然后调整大小,是安全的,只要这是修改向量内容的唯一线程。

锁定会自动暗示内存屏障,否则锁定没有多大意义。

答案 1 :(得分:3)

答案完全取决于AreAllVectorsEmpty的实施方式。

如果它只是检查一个可以原子设置的标志,那么是的,它是安全的。如果它迭代你想要改变的向量(或其他常用的容器),那么不,它是不安全的(迭代器会发生什么,如果向量在内部重新分配???)。

如果执行后者,则需要读/写锁机制,请查看shared mutexes

然后,您在检查之前获取共享锁,并在修改时获取独占锁。

请注意,如果areAllVectorsEmpty使用某些独立的数据结构(除了上面提到的原子标志),您可能必须使用单独的互斥来保护这个

答案 2 :(得分:2)

标准似乎没有要求这样做,比较http://en.cppreference.com/w/cpp/container#Thread_safety。如果它适用于您的特定编译器和STL?您需要查看来源。但我不会依赖它。

这让我想到了一个问题:你为什么要这样做?出于性能原因?你测量过性能吗?当你在调用AreAllVectorsEmpty之前锁定时,它真的是一个可衡量的性能影响吗?

顺便说一句,请不要直接锁定互斥锁,请使用std::lock_guard

答案 3 :(得分:0)

  

// AreAllVectorsEmpty:std :: all_of(data.begin(),data.end(),[](auto&amp;   v){return v.empty(); }

您正在访问内部向量的内部(调用空),同时另一个线程可以将一些元素插入到一个内部向量中 - &gt;数据竞赛