У меня есть приложение, в котором несколько потоков используют мьютекс.
std::lock_guard< std::recursive_mutex > lock(globalMutex_);
Один интенсивно (T1) остальные меньше (T2,T3 ..).
У меня есть пример, в котором потоки, которые требуют блокировки, реже блокируются за 100 секунд до успешного получения блокировки.
Протектор (T1 итак) которые приобретают блокировку часто делают это следующим образом:
void func()
{
std::lock_guard< std::recursive_mutex > lock(globalMutex_);
processing();
}
globalMutex_
тогда хорошо выпускается периодически.
Странное поведение:
T1 получить блокировку систематически в течение 100 секунд, в то время как другой поток вообще не получает блокировку
(В других потоках у меня тот же шаблон, но другой функционал вызывается реже)
Вопрос:
Чем это можно объяснить? Это нормальное поведение?
Контекст:
Я нахожусь под Windows 10 / последняя версия Visual Studio / 64 бит / GUI приложения
Замечания:
Даже если я положу T2 с высоким приоритетом ситуация такая же.
std::mutex
не дает никаких гарантий, что мьютексы заблокированы в том порядке, в котором их вызывают lock()
, Когда поток снимает блокировку, если поток быстро блокирует блокировку, тогда, если другой поток уже не ожидает блокировки и не выполняет в то же время, первый поток, вероятно, преуспеет в восстановлении блокировки.
Самое простое решение состоит в том, чтобы сохранить блокировки как можно более короткое время и попытаться убедиться, что каждый поток проводит хотя бы некоторое время без блокировки мьютекса.
Более сложным решением является создание собственного класса мьютекса, который обеспечивает некоторые гарантии порядка блокировки / разблокировки. Вы можете реализовать это с помощью комбинации std::mutex
а также std::condition_variable
,
Это выглядит как ошибка:
{
std::lock_guard< std::recursive_mutex > lock(globalMutex_);
processing();
}
Что значит processing()
делать? Если это занимает более нескольких микросекунд, то, вероятно, есть более эффективный способ решения вашей проблемы. Иногда это выглядит так:
bool success=false;
while (! success) {
auto result = speculative_processing();
{
std::lock_guard< std::recursive_mutex > lock(globalMutex_);
success = attempt_to_publish(result);
}
}
Часто бывает так, что отдельным потокам в многопоточной программе приходится выполнять дополнительную работу, чтобы не мешать друг другу. Но, стараясь не мешать друг другу, они могут лучше использовать несколько процессоров, и они быстрее справляются с работой.