У меня сложилось впечатление, что шаблон множественного чтения / записи, реализованный с помощью 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
Дает следующие характеристики.
{ 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