Что я пытаюсь сделать
Привет, у меня есть два типа потоков: основной и рабочие, где рабочие равны количеству ядер на ЦП, что я пытаюсь сделать, так это когда основной поток должен вызвать обновление, я установил логическое значение, называемое Обновление до true и вызов condition_variable(cv).notify_all
, тогда каждый поток выполнит свою работу, и когда это будет сделано, он увеличится на единицу atomic_int
с именем CoresCompleted, за которым следует cv.notify_all
, чтобы основной поток мог проверить, выполнена ли вся работа, затем он будет ждать переменной Updating быть ложным, чтобы быть уверенным, что все остальные потоки завершены, и он больше не обновляется, как только все будет сделано, основной поток устанавливает обновление в значение false и уведомляет всех.
КОД
Главный
void UpdateManager::Update() {
//Prepare Update
CoresCompleted = 0;
Updating = true;
//Notify Update Started
cv.notify_all();
//Wait for Update to end
auto Pre = high_resolution_clock::now();
cv.wait(lk, [] { return (int)UpdateManager::CoresCompleted >= (int)UpdateManager::ProcessorCount; });
auto Now = high_resolution_clock::now();
auto UpdateTime = duration_cast<nanoseconds>(Now - Pre);
//End Update and nofity threads
Updating = false;
cv.notify_all();
}
Рабочие
void CoreGroup::Work() {
Working = true;
unique_lock<mutex> lk(UpdateManager::m);
while (Working) {
//Wait For Update To Start
UpdateManager::cv.wait(lk, []{ return UpdateManager::Updating; });
if (!Working)
return;
//Do Work
size_t Size = Groups.size();
auto Pre = high_resolution_clock::now();
for (size_t Index = 0; Index < Size; Index++)
Groups[Index]->Update();
auto Now = high_resolution_clock::now();
UpdateTime = duration_cast<nanoseconds>(Now - Pre);
//Increment CoresCompleted And Notify All
UpdateManager::CoresCompleted++;
UpdateManager::cv.notify_all();
//Wait For Update To End
UpdateManager::cv.wait(lk, []{ return !UpdateManager::Updating; });
}
}
Проблема
Как только рабочие достигают последнего ожидания, где они ждут, когда обновление будет ложным, они застревают и никогда не уходят, по какой-то причине последний notify_all в основном потоке не достигает рабочих, я пытался искать и искал много примеров, но я не могу' не могу понять, почему он не срабатывает, может быть, я не понял, как работают cv и lock, есть идеи, почему это происходит и как это исправить?
@FrançoisAndrieux, не могли бы вы объяснить, что именно вы подразумеваете под блокировкой m? Я пытался заблокировать перед установкой обновления, но он просто зависает в основном потоке, что именно мне делать? при поиске примеров в Интернете я никогда не видел, чтобы кто-то блокировал m
См. en.cppreference.com/w/cpp/thread/condition_variable : «Даже если общая переменная является атомарной, она должна быть изменена в мьютексе, чтобы правильно опубликовать изменение в ожидающем потоке».UpdateManager::m
должен быть заблокирован при каждом изменении Update
. Вы можете разблокировать его между изменениями, чтобы потоки могли работать одновременно, но его необходимо снова заблокировать, прежде чем вносить в него дальнейшие изменения.
Вот как работает ваш код:
некоторое ожидание в Update
закончилось, когда было получено уведомление:
cv.wait(lk, [] { return (int)UpdateManager::CoresCompleted >= (int)UpdateManager::ProcessorCount; });
Он выходит из ожидания и требует блокировки мьютекса. Приступайте к выполнению своих действий, затем достигает конца и уведомляет другой поток, что они могут продолжить работу с этой строкой:
cv.notify_all();
Но это ложь, они не могут продолжать работу, потому что вы держите замок. Отпустите его, и они продолжат работу:
void UpdateManager::Update() {
<...>
//End Update and nofity threads
Updating = false;
lk.unlock();
cv.notify_all();
}
Вероятно, это не единственная проблема в этом коде, но я предполагаю, что вы блокируете мьютекс перед входом в метод Update
или имеете некоторую гарантию, что он запустится раньше другого (Work
).
Спасибо за ответ, так что раньше я не блокировался, но теперь я делаю это, я добавил lk.lock() в начале и разблокировал, где вы сказали, так что теперь он работает, но иногда он зависает с CoresCompleted, являющимся одним числом ниже ProcessorCount (15>=16) Я не уверен, почему это происходит, до использования метода уведомления этого никогда не происходило, так что это означает, что один из потоков не получает уведомление, есть идеи, почему это может быть?
@PedroS, вам нужно тщательно проверить, что вы блокируете и когда. Отладьте свою программу, чтобы найти проблему. То, что вы выложили, это лишь часть с довольно грязным кодом, который, честно говоря, у меня нет желания распутывать, тем более не зная, что именно вы изменили. Просто убедитесь, что вы блокируете общие переменные и не держите блокировку дольше, чем необходимо.
Вам нужно заблокировать
UpdateManager::m
, чтобы синхронизировать уведомление о событии, даже еслиUpdate
оказывается атомарным. В противном случае условие может пропустить событие уведомления.