请考虑以下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%的修正。
那么,你怎么纠正这种种族状况呢?如果有一个良好的推荐模式,请让我知道!
谢谢。
发布于 2008-12-04 12:20:24
最简单的解决方案是在fork()之前用sigprocmask()阻止SIGCHLD信号,并在处理pid之后在父代码中解除阻塞。
如果子节点死亡,则在解除阻塞信号后将调用SIGCHLD的信号处理程序。这是一个关键部分的概念--在您的例子中,关键部分在fork()之前开始,在children.add()之后结束。
发布于 2010-06-09 01:29:10
如果你不能使用关键片段,也许一个简单的计数器就能完成这个任务。+1当加法时,-1当移除时,不管先发生哪一个,当全部完成时,你最终可以得到零。
发布于 2008-12-04 11:46:49
除了现有的“儿童”之外,还增加了一个新的“早期死亡”数据结构。这将使孩子们的物品保持干净。
// 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
if (!earlyDeaths.contains(pid)) {
children.add(pid);
} else {
earlyDeaths.remove(pid);
}
}
}
}
// SIGCHLD handler
sigchld_handler(signo) {
while ( (pid = wait(status, WNOHANG)) > 0 ) {
print "parent caught SIGCHLD from ", pid
if (children.contains(pid)) {
children.remove(pid);
} else {
earlyDeaths.add(pid);
}
}
}编辑:如果您的进程是单线程的,这可以简化-- earlyDeaths不必是一个容器,它只需要保存一个pid。
https://stackoverflow.com/questions/340283
复制相似问题