给我的印象是,如果获取了太多共享锁,则使用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
答案 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中演示其行为的代码。