shared_mutex锁定顺序

scx*_*scx 7 c++ multithreading mutex locking c++17

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

探究cppreference之后,我不确定是这种情况。具体来说:

单个互斥锁上的所有锁定和解锁操作均以单个总顺序发生

例如,鉴于对进行以下操作shared_mutex,我认为unique_lock可能永远不会获得。假设无限数量的shared_locks,并且这些锁在第一次shared_locks发行之前获得。

shared_lock
shared_lock
shared_lock

unique_lock

shared_lock
[...]
shared_lock
Run Code Online (Sandbox Code Playgroud)

具有以下特征。

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

unique_lock
Run Code Online (Sandbox Code Playgroud)

但是,如果我正确理解了cppreference,那么一旦unique_lock尝试获取cppreference,continuous shared_locks将会阻塞直到unique_lock释放。提供以下线程特征。

{ shared_lock, shared_lock, shared_lock} // simultaneous

unique_lock

{ shared_lock, ..., shared_lock} // waits, then simultaneous
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

read
read
read
write
read
...
read
Run Code Online (Sandbox Code Playgroud)

How*_*ant 2

stdshared_mutex规范没有指定共享锁或唯一锁的优先级。也没有任何 API 可以设置这样的优先级。缺乏优先级规范的原始动机之一是Alexander Terekhov 算法的存在,如此处所述

第二个动机是解释 shared_mutex 中缺乏读写器优先级策略。这是由于 Alexander Terekhov 的一种算法,它让操作系统决定下一个获取锁的线程,而不关心正在寻找的是唯一锁还是共享锁。这导致读者完全缺乏或作者饥饿。这简直就是公平。

标准规范不需要 Alexander Terekhov 算法。然而,至少我希望该算法会成为首选,因为缺乏规范或 API 来优先选择读者而不是作者,反之亦然。

有关 Alexander Terekhov 算法的更多详细信息以及一些演示其行为的代码,请参阅此处的 SO 答案