Обратите внимание на следующий fork()
/ SIGCHLD
псевдокод.
// main program excerpt
for (;;) {
if ( is_time_to_make_babies ) {
pid = fork();
if (pid == -1) {
/* fail */
} else if (pid == 0) {
/* child stuff */
print "child started"
exit
} else {
/* parent stuff */
print "parent forked new child ", pid
children.add(pid);
}
}
}
// SIGCHLD handler
sigchld_handler(signo) {
while ( (pid = wait(status, WNOHANG)) > 0 ) {
print "parent caught SIGCHLD from ", pid
children.remove(pid);
}
}
В приведенном выше примере есть условие гонки. Возможно, что «/* child stuff */
» завершится до запуска «/* parent stuff */
», что может привести к тому, что pid ребенка будет добавлен в список детей после его выхода и никогда не будет удален. Когда придет время закрывать приложение, родитель будет бесконечно ждать окончания работы уже готового потомка.
Одним из решений, которое я могу придумать, чтобы противостоять этому, является наличие двух списков: started_children
и finished_children
. Я бы добавил к started_children
в том же месте, которое сейчас добавляю к children
. Но в обработчике сигналов вместо удаления из children
я бы добавил к finished_children
. Когда приложение закрывается, родитель может просто подождать, пока разница между started_children
и finished_children
не станет равной нулю.
Другое возможное решение, о котором я могу подумать, - это использование разделяемой памяти, например, поделитесь списком детей родителей и пусть дети .add
и .remove
сами? Но я не слишком много знаю об этом.
РЕДАКТИРОВАТЬ: Другое возможное решение, которое было первым, что пришло в голову, это просто добавить sleep(1)
в начале /* child stuff */
, но это пахнет смешно для меня, именно поэтому я пропустил его. Я также даже не уверен, что это 100% исправление.
Итак, как бы вы исправили это состояние гонки? И если для этого есть устоявшийся рекомендуемый шаблон, пожалуйста, дайте мне знать!
Спасибо.