有没有办法杀死GHCi会话中的所有分叉线程而不重新启动它?

时间:2014-07-28 16:19:53

标签: multithreading haskell ghc yesod

基于my previous question我想询问是否有办法在GHCi会话中杀死所有用户创建的线程?

原因是当一个函数退出GHCi时,它产生的线程不会自动终止,即使通过代码重新加载也会持久化。重新启动GHCi可以解决这个问题,但是由于我的应用程序需要一段时间才能加载,所以如果有可能(甚至是hacky)的解决方法,这将是很好的。

1 个答案:

答案 0 :(得分:4)

不,实际上和How can I build a ThreadId given that I know the actual number?中的原因几乎相同:图书馆根本没有给你任何东西来获取所有(仍在运行的)线程的ThreadId或任何其他设施处理任何不属于你的线程。

此外,您无法可靠地猜测使用forkIO生成的哪些线程属于您的GHCi会话(所有评估通常在forkIO中沙箱化),底层的yesod应用程序或者线程RTS(具有至少调用forkIO - 这基本上可以确保所有事件管理器都关闭。目前,这还不错,因为GHCi在main线程中运行,如果关闭,IO管理器会重新启动,但在未来的版本中可能会发生变化。

那么为什么线程甚至会在终止时被收集? hs_exit()。从本质上讲,它会调用ioManagerDie()(删除所有event managers)和exitScheduler(..)(请参阅scheduler,基本上kills all threads。这些函数都没有适当的FFI包装。

在调用hs_exit()时,来自Haskell世界的main已经完成。由于这些函数中没有一个在GHC.*模块中具有适当的等价物,因此不能直接在Haskell中调用它们,因此在GHCi中,因为没有合适的:#命令。

总而言之,你做不到。如果要添加一个命令来重新启动GHCi中的调度程序,那就很容易了。但鉴于调度程序已在hs_init()中启动并在RTS modelhs_exit()中停止,我怀疑这是一个简单的扩展。


根据您的想法,您可以作弊。您可以编写自己的forkIOMem,将ThreadId存储在全局MVar中,并替换源中的所有forkIO。这可能非常很麻烦,特别是如果您正在使用库,因为您需要确保{em>在任何地方替换forkIO

当然,你可以插入base包,但这可能是疯狂的(尽管仍然可能),在那里更改forkIO并将killAllforkIOs添加到Control.Concurrent(我已经说过这可能是疯了,对吧?)