shared_mutex锁定顺序

时间:2019-05-11 15:28:20

标签: c++ multithreading locking c++17 mutex

给我的印象是,如果获取了太多共享锁,则使用c ++ 17的std::shared_mutex实现的多读取器/单写入器模式可能永远不会放弃唯一锁。

在深入研究cppreference之后,我不确定情况是否如此。具体来说:

  

单个互斥锁上的所有锁定和解锁操作都在单个   总订单

例如,考虑到对shared_mutex的以下操作,我认为unique_lock可能永远不会获得。假设无限量的shared_locks,并且这些锁在第一个shared_locks发行之前就已经获得。

shared_lock
shared_lock
shared_lock

unique_lock

shared_lock
[...]
shared_lock

具有以下特征。

{ shared_lock, shared_lock, shared_lock, shared_lock, ..., shared_lock } // never releases

unique_lock

但是,如果我正确理解cppreference,一旦unique_lock尝试获取时,连续的shared_locks会阻塞,直到释放unique_lock为止。提供以下线程特征。

{ shared_lock, shared_lock, shared_lock} // simultaneous

unique_lock

{ shared_lock, ..., shared_lock} // waits, then simultaneous

所以我的问题是,std::shared_mutex是否保持共享锁和唯一锁之间的顺序?防止由于大量unique_locks被获取而从未获取shared_locks的情况。

编辑:

这里是一个代码示例,可以帮助您理解问题,并且为了后代。在MSVC 2019上,shared_mutex是安全的,可以根据需要进行排序。 unique_lock确实在shared_locks的“无限”数量之前得到处理。

现在的问题是,这个平台依赖吗?

#include <chrono>
#include <cstdio>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <vector>

using namespace std::chrono_literals;

std::shared_mutex smtx;

int main(int, char**) {

    std::vector<std::thread> threads;

    auto read_task = [&]() {
        std::shared_lock l{ smtx };
        printf("read\n");
        std::this_thread::sleep_for(1s);
    };

    auto write_task = [&]() {
        std::unique_lock l{ smtx };
        printf("write\n");
        std::this_thread::sleep_for(1s);
    };

    // Create a few reader tasks.
    threads.emplace_back(read_task);
    threads.emplace_back(read_task);
    threads.emplace_back(read_task);


    // Try to lock a unique_lock before read tasks are done.
    std::this_thread::sleep_for(1ms);
    threads.emplace_back(write_task);

    // Then, enque a gazillion read tasks.
    // Will the unique_lock be locked? [drum roll]

    // Would be while(true), 120 should be enough for demo
    for (size_t i = 0; i < 120; ++i) {
        std::this_thread::sleep_for(1ms);
        threads.emplace_back(read_task);
    }

    for (auto& t : threads) {
        t.join();
    }
}

输出:

read
read
read
write
read
...
read

1 个答案:

答案 0 :(得分:2)

std shared_mutex规范未指定共享锁或唯一锁的优先级。也没有任何API可以设置这种优先级。缺乏优先级规范的原始动机之一是Alexander Terekhov algorithm as explained here的存在。

  

第二个动机是解释读者-作家的缺乏   shared_mutex中的优先级策略。这是由于算法   归功于Alexander Terekhov,它可以让操作系统决定哪个线程   是接下来获得该锁而无需关心是否是唯一锁或   正在寻求共享锁。这导致完全缺乏读者   或作家饥饿。这很公平。

标准规范不需要Alexander Terekhov算法。但是,至少我希望,由于缺少规范或API而不喜欢读者而不是作家,该算法将成为首选算法。

有关Alexander Terekhov算法的更多详细信息,以及一些在this SO answer here中演示其行为的代码。