Блокировка слишком длинная

У меня есть приложение, в котором несколько потоков используют мьютекс.

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 с высоким приоритетом, ситуация такая же.

2 ответа

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);
    }
}

Часто бывает так, что отдельным потокам в многопоточной программе приходится выполнять дополнительную работу, чтобы не мешать друг другу. Но, стараясь не мешать друг другу, они могут лучше использовать несколько процессоров, и они быстрее справляются с работой.

Вы достигнете своей цели с помощью condition_variable.

std::condition_variable cv;
bool busy = false;
void func()
{
  {
    std::unique_lock<std::mutex> lk(globalMutex_);
    cv.wait(lk, []{return !busy;});
    busy = true;
  }
  processing();
  busy = false;
  cv.notify_one();
}
Другие вопросы по тегам