Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, nen777w, Вы писали:
N>>Нужно была гарантия того что notofy_one() будет вызвано уже для ожидающей cv у thread_worker
К>Нужна была гарантия того, что у тебя не возникает гонка на доступ в переменную m_state.
Есть condition_variable, есть mutex на котором она ожидает с проверкой условия от ложного срабатывания.
Другой поток сперва устанавливает это условие а потом делает notify_one на этой переменной.
в результате.. condition variable продолжаеn висеть на этом мютексе как буд то бы никакого notify_one и не было...
Есть идеи?
Поток, делающий wait, точно один?
Если notify был вызван до wait'а, то нет никакой гарантии, что ожидающий поток будет когда-либо пробуждён.
Всё делаете, как показано в примере?
B>Если notify был вызван до wait'а, то нет никакой гарантии, что ожидающий поток будет когда-либо пробуждён.
гм... похоже это мой случай.
У меня дело в том что несколько рабочих потоков и у каждого свой cv.
И основной (менеджер) тоже со своей CV который менеджит их и готовит задания.
Задумка была в том что посредством notify_one на ожидающей CV менеджера, тот что выполняет задание сообщает менеджеру что он его выполнил и готов принять еще (а сам в этом время ждет на своей cv), а менеджер в свою очередь загружает новое задание и в его cv сообщает что пора начинать работу.
Вот куско кода рабочего потока:
do
{
notify_cb.fn_notify_ready(shared_from_this()); //<-- сообщаем менеджеру что готовы принять работу или выполнили ее
// <-- похоже тут менеджер успевает установить задачу и сообщить - выполняй
boost::unique_lock<boost::mutex> lock(m_mtx_data_ready); //<-- ждем пуша от менеджера
m_cv_data_ready.wait(lock, [this]() -> bool { return s_DataReady == m_state || s_Finalize == m_state; });
if (s_DataReady == m_state)
{
do_job();
m_state = s_DataProcessed;
}
} while (s_Finalize != m_state);
N>гм... похоже это мой случай.
Тогда добавляйте отладочную печать и вперде.
N>Задумка была в том что посредством notify_one на ожидающей CV менеджера, тот что выполняет задание сообщает менеджеру что он его выполнил и готов принять еще (а сам в этом время ждет на своей cv), а менеджер в свою очередь загружает новое задание и в его cv сообщает что пора начинать работу.
Какой-то самописный task pool получается. Почему бы не использовать готовые решения?
Здравствуйте, nen777w, Вы писали:
N>Есть condition_variable, есть mutex на котором она ожидает с проверкой условия от ложного срабатывания. N>Другой поток сперва устанавливает это условие а потом делает notify_one на этой переменной. N>в результате.. condition variable продолжаеn висеть на этом мютексе как буд то бы никакого notify_one и не было... N>Есть идеи?
Я бы напихал туда логов, чисто чтобы убедиться, что пробуждения, хотя бы и ложные, случаются.
Самое простое, что может случиться, — это ты не тот объект кормишь! Для этого и надо последить за this.
Ну, или более детально — последить за соответствием членов — мьютекса и кондишена.
Второе, — это детская ошибка, когда кондишен дёргается до изменения условия.
Кроме того, у тебя условие составное: s_DataReady == m_state || s_Finalize == m_state, — значит, надо дёргать кондишен на присваивании этой переменной m_state обоих значений.
В конце концов, можно пожертвовать ложными пробуждениями и дёргать кондишен на присваивании любого значения, а не только полезного.
На десерт — всякие расстрелы памяти, из-за чего кондишен сломался под корень.
К>Я бы напихал туда логов, чисто чтобы убедиться, что пробуждения, хотя бы и ложные, случаются.
Спасибо, да попробую залогировать, поанализировать что получится. К>Самое простое, что может случиться, — это ты не тот объект кормишь! Для этого и надо последить за this. К>Ну, или более детально — последить за соответствием членов — мьютекса и кондишена.
М.. да вроде такого не должно случаться. К>Второе, — это детская ошибка, когда кондишен дёргается до изменения условия.
Это вряд ли. К>Кроме того, у тебя условие составное: s_DataReady == m_state || s_Finalize == m_state, — значит, надо дёргать кондишен на присваивании этой переменной m_state обоих значений.
Так и есть.
Здравствуйте, nen777w, Вы писали:
N>в результате.. condition variable продолжаеn висеть на этом мютексе как буд то бы никакого notify_one и не было... N>Есть идеи?
ставлю на то, что notify_one вызывается до wait, это самый частый прокол с condition variables
Здравствуйте, nen777w, Вы писали:
N>Нужно была гарантия того что notofy_one() будет вызвано уже для ожидающей cv у thread_worker
Нужна была гарантия того, что у тебя не возникает гонка на доступ в переменную m_state.
Которую ты решил, пропустив поток источника в критическую секцию, под мьютекс. Что, в общем-то, и надо было сделать с самого начала.
Но кстати, критические секции неспроста названы критическими. Находиться внутри них нужно как можно меньшее время.
А то у тебя получается следующее:
— приёмник захватывает мьютекс и входит в цикл
— приёмник проходит через условие (допустим, с первого раза у него это получилось)
— заходит внутрь job() и там кукует
— источник собирается дать ему отмашку и висит перед мьютексом
. . . . .
— приёмник выходит из job(), в цикле заходит в условие и отпускает мьютекс
— источник наконец получает управление, меняет условие и убегает
— приёмник просыпается, хватает мьютекс, проверяет условие, идёт дальше
Желательно локализовать доступ к флажку. А если job() требует защиты, — защищать его отдельной секцией с отдельным мьютексом.
Вопрос на засыпку: что должно происходить, если job() ещё выполняется, а новые данные уже пришли? В какое состояние должен перейти объект по окончании job(), — s_DataReady, s_DataProcessed?
Мы сейчас не рассматриваем проблему гонок, защиты и всего такого. Допустим, с этим всё хорошо. Будем моделировать семантику, а уж потом обвяжем синхрообъектами для достижения этой семантики.
Логично, что следует разнести состояние объекта на состояние входа (неготово/готово) и выхода (недоделано/доделано).
Кстати сказать, конвеер можно было бы выразить в терминах очередей.
Которые выражаются через буфер и обвязку — из пары семафоров, либо из мьютекса, условной переменной и флажка/счётчика.
А аварийное завершение сделать через thread::interrupt(), вместо подмешивания признака "пора валить" во все очереди и барьеры.