Увеличить общий_блок. Читать предпочтительнее?
Я проверял библиотеку наддува (версия 1.45) на предмет блокировки чтения / записи. Когда я запускал свои тесты на нем, казалось, что shared_ptr предпочитал потоки моего читателя, то есть когда мой писатель пытался взять блокировку для своей работы, он не останавливал последующие чтения.
Возможно ли в boost изменить это поведение?
using namespace std;
using namespace boost;
mutex outLock;
shared_mutex workerAccess;
bool shouldIWork = true;
class WorkerKiller
{
public:
void operator()()
{
upgrade_lock<shared_mutex> lock(workerAccess);
upgrade_to_unique_lock<shared_mutex> uniqueLock(lock);
cout << "Grabbed exclusive lock, killing system" << endl;
sleep(2);
shouldIWork = false;
cout << "KILLING ALL WORK" << endl;
}
private:
};
class Worker
{
public:
Worker()
{
}
void operator()()
{
shared_lock<shared_mutex> lock(workerAccess);
if (!shouldIWork) {
outLock.lock();
cout << "Workers are on strike. This worker refuses to work" << endl;
outLock.unlock();
} else {
sleep(1);
outLock.lock();
cout << "Worked finished her work" << endl;
outLock.unlock();
}
}
};
int main(int argc, char* argv[])
{
Worker w1;
Worker w2;
Worker w3;
Worker w4;
WorkerKiller wk;
boost::thread workerThread1(w1);
boost::thread workerThread2(w2);
boost::thread workerKillerThread(wk);
boost::thread workerThread3(w3);
boost::thread workerThread4(w4);
workerThread1.join();
workerThread2.join();
workerKillerThread.join();
workerThread3.join();
return 0;
}
И вот вывод каждый раз:
Работал закончил свою работу
Работал закончил свою работу
Работал закончил свою работу
Работал закончил свою работу
Схватил эксклюзивный замок, система убийства
УБИТЬ ВСЕ РАБОТЫ
Мое требование
Если бы автор попытался получить эксклюзивную блокировку, я бы хотел, чтобы все предыдущие операции чтения были завершены. А затем все последующие операции чтения заблокировать.
2 ответа
Я немного опоздал на этот вопрос, но я считаю, что у меня есть некоторая соответствующая информация.
Предложения shared_mutex
Комитету C++, на котором основаны надстройки надстройки, целенаправленно не был указан API, чтобы дать читателям или писателям приоритет. Это потому, что Александр Терехов около десяти лет назад предложил совершенно справедливый алгоритм. Это позволяет операционной системе решать, является ли следующий поток для получения мьютекса средством чтения или записи, а операционная система совершенно не знает, является ли следующий поток средством чтения или записи.
Из-за этого алгоритма отпадает необходимость указывать, является ли читатель или писатель предпочтительным. Насколько мне известно, в настоящее время буст-библиотеки (boost 1.52) реализованы с помощью этого честного алгоритма.
Алгоритм Терехова состоит в том, что мьютекс чтения / записи состоит из двух элементов: gate1 и gate2. Только один поток за раз может проходить через каждые ворота. Ворота могут быть реализованы с помощью мьютекса и двух условных переменных.
И читатели, и писатели пытаются пройти через ворота1. Для прохождения через gate1 должно быть верно, что поток записи в настоящее время не находится внутри gate1. Если есть, поток пытается пройти через блоки gate1.
Как только поток читателя проходит через gate1, он получает право владения мьютексом.
Когда поток записи проходит через gate1, он также должен пройти через gate2, прежде чем получить право на запись для мьютекса. Он не может пройти через gate2, пока количество читателей внутри gate1 не упадет до нуля.
Это справедливый алгоритм, потому что когда в gate1 имеется только 0 или более считывателей, операционная система должна решать, является ли следующий поток, попадающий внутрь gate1, считывателем или записывающим устройством. Автор становится "приоритетным" только после того, как он прошел через gate1, и, таким образом, становится следующим в очереди, чтобы получить право собственности на мьютекс.
Я использовал ваш пример, скомпилированный против примера реализации того, что в итоге стало shared_timed_mutex
в C++14 (с небольшими изменениями в вашем примере). Код ниже называет это shared_mutex
это имя, которое у него было, когда оно было предложено.
Я получил следующие выводы (все с тем же исполняемым файлом):
Иногда:
Worked finished her work
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Workers are on strike. This worker refuses to work
Workers are on strike. This worker refuses to work
И иногда:
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Workers are on strike. This worker refuses to work
Workers are on strike. This worker refuses to work
Workers are on strike. This worker refuses to work
И иногда:
Worked finished her work
Worked finished her work
Worked finished her work
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Я считаю, что теоретически возможно также получить другие результаты, хотя я не подтверждал это экспериментально.
В целях полного раскрытия, вот точный код, который я выполнил:
#include "../mutexes/shared_mutex"
#include <thread>
#include <chrono>
#include <iostream>
using namespace std;
using namespace ting;
mutex outLock;
shared_mutex workerAccess;
bool shouldIWork = true;
class WorkerKiller
{
public:
void operator()()
{
unique_lock<shared_mutex> lock(workerAccess);
cout << "Grabbed exclusive lock, killing system" << endl;
this_thread::sleep_for(chrono::seconds(2));
shouldIWork = false;
cout << "KILLING ALL WORK" << endl;
}
private:
};
class Worker
{
public:
Worker()
{
}
void operator()()
{
shared_lock<shared_mutex> lock(workerAccess);
if (!shouldIWork) {
lock_guard<mutex> _(outLock);
cout << "Workers are on strike. This worker refuses to work" << endl;
} else {
this_thread::sleep_for(chrono::seconds(1));
lock_guard<mutex> _(outLock);
cout << "Worked finished her work" << endl;
}
}
};
int main()
{
Worker w1;
Worker w2;
Worker w3;
Worker w4;
WorkerKiller wk;
thread workerThread1(w1);
thread workerThread2(w2);
thread workerKillerThread(wk);
thread workerThread3(w3);
thread workerThread4(w4);
workerThread1.join();
workerThread2.join();
workerKillerThread.join();
workerThread3.join();
workerThread4.join();
return 0;
}
Поиск в Google по запросу "голодание с использованием общих блокировок" показал эту ссылку:
Похоже, "обновление" может быть ключом. Смотрите также: