为了便于讨论,这是一个简短的例子。它产生Async
,其中有26个并发子Async
。
import Control.Concurrent (threadDelay)
import Control.Concurrent.Async (async, cancel, forConcurrently_)
import Control.Exception (finally)
import Data.Char (toUpper)
import Prelude ((*), (*>), ($))
import System.IO (IO, hPutStr, stderr)
main :: IO ()
main = do
a <- async $ forConcurrently_ ['a'..'z'] $ \c ->
(print c *> delay 2) `finally` print (toUpper c)
delay 1
cancel a
where
delay sec = threadDelay (sec * 1000000)
print c = hPutStr stderr [c]
每个孩子
finally
时,应始终打印该字母的大写版本(代表“清理”操作)。然后,我们暂停足够长的时间让所有Async
启动,但不足以让它们完成,然后在父项上调用cancel
,从而为每个Async
抛出一个异常finally
s。
我希望每个abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
操作都能运行,从而产生输出
abcdefghijklmnopqrstuvwxyzABC
但我得到了
a
我如何推断这个例子中发生的事情,以及获得我正在寻找的那种可靠清理行为的正确方法是什么?
答案 0 :(得分:2)
您使用的是什么版本的async
? cancel
中2.1.1
的行为已更改为等待线程实际退出。在早期版本中,它只抛出异步异常并退出。在您的情况下,所有线程都收到异常,但并非所有线程都有机会处理它(打印上面的char),因为main
已经退出。尝试添加例如delay 10
之后的cancel
。如果您使用2.1.1
,那么它可能(或可能不)表示async
中的错误。
增加:它确实看起来像一个错误:https://github.com/simonmar/async/issues/59#issuecomment-289894612