shared_mutex 锁排序

shared_mutex lock ordering

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

cppreference 上进行挖掘后,我不确定情况是否如此。具体来说:

All lock and unlock operations on a single mutex occur in a single total order

例如,给定 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 是否在共享锁和唯一锁之间保持排序?防止由于获得压倒性数量的 shared_locks 而导致 unique_locks 从未获得的情况。

编辑:

这是一个代码示例,可帮助理解问题,并为后代着想。在 MSVC 2019 上,shared_mutex 是安全的,并且可以根据需要进行排序。 unique_lock 确实在 "infinite" 数量的 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

std shared_mutex 规范没有指定共享锁或唯一锁的优先级。也没有任何 API 来设置这样的优先级。缺乏优先级规范的最初动机之一是 Alexander Terekhov algorithm as explained here.

的存在

A secondary motivation is to explain the lack of reader-writer priority policies in shared_mutex. This is due to an algorithm credited to Alexander Terekhov which lets the OS decide which thread is the next to get the lock without caring whether a unique lock or shared lock is being sought. This results in a complete lack of reader or writer starvation. It is simply fair.

标准规范不需要 Alexander Terekhov 算法。然而,至少我希望,由于缺乏规范或 API 更喜欢读者而不是作者或 vice-versa.

,这种算法将是首选

this SO answer here 中有更多关于 Alexander Terekhov 算法和演示其行为的代码的详细信息。