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 算法和演示其行为的代码的详细信息。
我的印象是,如果获取了太多共享锁,使用 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 算法和演示其行为的代码的详细信息。