Пожалуйста, рассмотрите следующий псевдокод 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 сами? Но я не слишком много знаю об этом.
EDIT: Еще одно возможное решение, которое первым делом пришло в голову, - просто добавить sleep(1) в начале /* child stuff */, но это пахнет мне смешно, и именно поэтому я его оставил. Я даже не уверен, что это 100% -ное исправление.
Итак, как бы вы исправили это состояние гонки? И если у вас есть хорошо зарекомендовавший себя образец, сообщите мне об этом!
Спасибо.