2013-04-09 6 views
6

У меня проблема с тупиком в моем коде, связанная с использованием переменных условия. Это скорее вопрос дизайна, чем вопрос с чистым кодом. У меня нет проблем с написанием кода, как только я понимаю правильный дизайн. У меня есть следующий сценарий:Условие deadlock

  1. Резьба A ждет переменную условия.
  2. Thread B вызывает notify_all, и поток A просыпается.

Это, конечно, то, что я хочу, и что происходит, когда все работает так, как ожидалось. Но иногда вместо этого получается следующий сценарий:

  1. Резьба A выполняет код прямо перед тем, как он начнет ждать переменной условия.
  2. Thread B вызывает notify_all, думая, что поток A ждет.
  3. Нить А начинает ждать переменную условия, не понимая, что поток B уже сказал ей прекратить ждать. Тупик.

Каков наилучший способ решить эту проблему? Я не могу придумать надежный способ проверить, действительно ли поток A ожидает ожидания, чтобы узнать, когда я должен позвонить notify_all в поток B. Нужно ли мне прибегать к timed_lock? Я ненавижу.

+0

В какой библиотеке вы используете? Какая ОС? –

+0

Используйте семафор. –

ответ

5

В течение периода перед тем, как Thread A ожидает переменную condition, она должна содержать мьютексы. Самое простое решение - убедиться, что Thread B имеет один и тот же мьютекс в то время, когда он вызывает notify_all. Так что-то вроде этого:

std::mutex m; 
std::condition_variable cv; 
int the_condition = 0; 

Thread A: { 
    std::unique_lock<std::mutex> lock(m); 
    do something 
    while (the_condition == 0) { 
    cv.wait(lock); 
    } 
    now the_condition != 0 and thread A has the mutex 
    do something else 
} // releases the mutex; 

Thread B: { 
    std::unique_lock<std::mutex> lock(m); 
    do something that makes the_condition != 0 
    cv.notify_all(); 
} // releases the mutex 

Это гарантирует, что Thread B делает только notify_all() либо до Thread А приобретает мьютекс или в то время как Thread А ожидание переменной состояния.

Другой ключ здесь, однако, является циклом while, ожидающим, когда условие станет истинным. После того, как A имеет мьютекс, не должно быть возможным, чтобы какой-либо другой поток изменил условие, пока A не проверил условие, не нашел его ложным и начал ждать (таким образом, высвобождая мьютекс).

Дело в том, что вы действительно ожидаете, что значение этого условия станет ненулевым, std :: condition_variable :: notify_all просто сообщает вам, что поток B считает, что поток A должен просыпаться и повторно тестироваться.

+3

+1 Обратите внимание, что 'std :: condition_variable :: wait' также имеет перегрузку, которая принимает предикат. Поэтому вместо 'while (the_condition == 0) cv.wait (lock);' вместо этого вы можете написать 'cv.wait (lock, [&] {return the_condition! = 0;});' –

+0

@AndrewDurward: Это мило ! Я этого не знал. –

+1

Ах да, конечно. Смешно просто. Просто заблокируйте тот же мьютекс, который использует переменная условия при изменении условия, чтобы на самом деле быть истинным в потоке B. Я реализовал ваше предложение в своем коде, и оно решает проблему. Спасибо! –

2

Переменная состояния всегда должна быть связана с мьютексом, чтобы избежать условия гонки, созданного одним потоком, готовым подождать, и другой поток, который может сигнализировать о состоянии до того, как на самом деле первый поток на самом деле ждет его, что приведет к тупиковой ситуации. Поток будет постоянно ждать сигнала, который никогда не отправляется. Любой мьютекс может быть использован, нет явной связи между мьютексом и переменной условия.