【问题标题】:What's the best way to exit a Haskell program?退出 Haskell 程序的最佳方法是什么?
【发布时间】:2013-06-01 12:50:48
【问题描述】:

我有一个使用多个线程的程序。据我了解,当线程 0 退出时,整个程序都会退出,而不管其他任何可能仍在运行的线程。

问题是,这些其他线程可能打开了文件。自然地,这包含在异常处理代码中,在出现问题时会干净地关闭文件。这也意味着如果我使用killThread(通过throwTo 实现),文件应该在线程退出之前关闭。

我的问题是,如果我只是让线程 0 退出,而不试图停止其他线程,那么所有各种文件句柄都会很好地关闭吗?是否有任何缓冲输出被刷新?

简而言之,我可以直接退出,还是需要先手动杀死线程?

【问题讨论】:

  • 我不希望依赖killThreadexit 来清理其他线程,这只是为了保持良好的编程卫生。我检测所有线程以监听来自主线程的清理信号,以便它们都可以正常关闭。
  • 为什么不试试呢? IME,大多数操作系统将关闭文件(即不留下句柄、fd 或其他泄漏),但未刷新的 I/O 不会被刷新。
  • @GabrielGonzalez 如果您这样做,则意味着线程只会在某些点终止。如果您忘记定期检查,或者某些事情花费的时间比预期的要长,您可能会遇到这样的情况,即您向线程发出终止信号,但实际上并没有(或者只是需要很长时间)。无论如何你都需要处理异常,那么为什么不使用killThread呢?
  • @MathematicalOrchid 非线程程序也是如此:它只会在某些点终止。如果您想保证正常和及时的恢复,您可以将操作超时。

标签: multithreading haskell


【解决方案1】:

您可以使用Control.Concurrent.MVar 来实现此目的。 MVar 本质上是一个“空”或“满”的标志。线程可以尝试读取MVar,如果它为空,则阻塞线程。无论您在哪里有一个执行文件 IO 的线程,都为其创建一个 MVar,并将其传递给 MVar 作为参数。将您创建的所有MVars 放入一个列表中:

main = do
  let mvars = sequence (replicate num_of_child_threads newEmptyMVar)
  returnVals <- sequence (zipWith (\m f -> f m) 
                                  mvars 
                                  (list_of_child_threads :: [MVar -> IO a])) 

一旦子线程完成了您担心的所有文件操作,请写入MVar。而不是写killThread 你可以这样做

mapM_ takeMVar mvars >> killThread 

否则无论你的线程会在哪里退出,只需获取所有MVars。

有关详细信息,请参阅documentation on GHC concurrency

【讨论】:

  • 我认为他在问一个不同的问题:如果主线程完成并且不再想等待它们的结果,如何优雅地停止子线程。旁注:您可以使用async 包执行您所描述的操作,如果您在子线程中使用wait,它还可以重新引发在子线程中创建的异常。
  • 当我读到它时,他担心当父线程退出时,子线程也会退出,如果他们正在写入文件,可能会导致不必要的副作用。如果主线程等待其子线程完成所有文件 IO 操作,这将不再是问题。
【解决方案2】:

从我的测试中,我发现了一些事情:

  1. exitFailure 和朋友们只在线程 0 中工作。(文档实际上是这样说的,如果你不厌其烦地阅读它。这些函数只是抛出异常,在其他线程中会被默默地忽略。)

  2. 如果异常终止您的线程或整个程序,则任何打开的句柄都不会刷新。当您拼命想弄清楚程序崩溃的确切位置时,这非常烦人!

所以如果你想在程序退出之前刷新你的东西,那么必须实现它。只是让线程 0 死掉不会刷新东西,不会抛出任何异常,只是静默终止所有线程而不运行异常处理程序。

【讨论】:

    猜你喜欢
    • 2013-04-21
    • 2014-12-11
    • 1970-01-01
    • 2011-12-24
    • 2012-03-20
    • 2015-04-06
    • 2016-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多