【发布时间】:2017-11-20 21:04:38
【问题描述】:
if @block
rd, wr = IO.pipe
@pid = fork do
$0 = "Forked child from Page #{@path}"
rd.close
result = @block.call(@resp.body)
begin
wr.write Marshal.dump(result)
end
wr.close
这是用叉子共享管道的一种非常标准的方式,但是一旦调用rd.close,它就会破坏管道以供wr 使用。直到那条线,管道才能正常工作(我用 Pry 逐行运行它)。据我所知,关闭分叉内的阅读器以阻止它干扰发送 EOF 是一种很好的做法(我不知道为什么会这样,我只知道这是这种做法)。
这是我调用到生产应用程序中的库的一部分。库自己的规范从未遇到过这种情况,即使它们运行非常相似的代码(只有 @block 和 @resp 会有很大的不同)。显然,应用程序的代码更复杂,但我看不出它会如何干扰这段代码。我搜索了该应用所需的其他库,以查看是否有任何捕获可能干扰此的信号,但我一无所获。
任何人都可以提出问题可能是什么或解决方法吗?我已经尝试捕获 Errno::EPIPE 异常和 retrying 但这并不能解决它,重新打开管道(我不完全确定如何做到这一点,因为在分叉发生后很难链接它到主进程),清空块,所以它不做任何工作......仍然没有乐趣。
我还发现(通过对 this question 的评论)标准库中的 Ruby 的 Open3 默默地拯救并删除了 Errno::EPIPE,但提交消息没有给出任何理由。不知道有没有关系。
https://github.com/ruby/ruby/blob/e3c288569833b6777e7ecc0bbc26f8e6ca8f2ba7/lib/open3.rb#L268
【问题讨论】:
-
请注意,
fork会返回两次,一次在父项中(返回子项的 pid),一次在子项中(返回 nil)。因此,您应该在条件内调用fork,即:if fork then <parent code> else <child code> end。IO.pipe的文档包含一个示例。 -
@Stefan 谢谢。确实如此,但这不是构建代码的唯一方法,例如this code 与我的非常相似。在我的代码中,作者在 fork 之后立即关闭,不需要 if/else。
-
你是对的,我从来没有使用过
fork的块形式。 -
当孩子尝试写入管道时,您确定主进程(使
fork调用)存在吗?如果没有任何阅读器,则不能写入管道,反之亦然。例如,您可以通过Process#wait调用阻止父进程并等待孩子死亡。 -
你能提供一个minimal reproducible example吗?