Пожалуйста, рассмотрите следующий псевдокод 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% -ное исправление.
Итак, как бы вы исправили это состояние гонки? И если у вас есть хорошо зарекомендовавший себя образец, сообщите мне об этом!
Спасибо.