use*_*220 5 concurrency multithreading haskell coroutine
我试图理解Coroutines但是由于forkIO存在线程,所以我不太了解它们的用途.什么用例确实需要在线程上使用协同程序?
如果您正在讨论特定的Haskell协程实现(如果是,请添加链接)或关于一般概念,那么您的问题有点不清楚.
使用forkIO和某种线程间通信是实现协同程序的一种方法.优点是,这种方式可以利用多个CPU /核心,但在我看来,有几个缺点:
IO基于显式并发,因此所有计算都必须在IOmonad中运行.我进一步假设你的问题是关于这个Coroutine实现.
让我举一个小例子.假设我们想要计算大因子,但由于计算可能需要很长时间,我们希望它在每个周期后暂停,以便我们可以向用户提供一些反馈.此外,我们想要发出剩余计算周期的信号:
import Control.Monad
import Control.Monad.Coroutine
import Control.Monad.Coroutine.SuspensionFunctors
import Control.Parallel
import Data.Functor.Identity
-- A helper function, a monadic version of 'pogoStick':
-- | Runs a suspendable 'Coroutine' to its completion.
pogoStickM :: Monad m => (s (Coroutine s m x) -> m (Coroutine s m x))
-> Coroutine s m x -> m x
pogoStickM spring c = resume c >>= either (pogoStickM spring <=< spring) return
factorial1 :: (Monad m) => Integer -> Coroutine (Yield Integer) m Integer
factorial1 = loop 1
where
loop r 0 = return r
loop r n = do
let r' = r * n
r' `par` yield n
(r' `pseq` loop r') (n - 1)
run1 :: IO ()
run1 = pogoStickM (\(Yield i c) -> print i >> return c) (factorial1 20) >>= print
Run Code Online (Sandbox Code Playgroud)
现在让我们说我们意识到在每个周期之后屈服效率太低.相反,我们希望调用者在再次暂停之前决定我们应该计算多少个周期.为此,我们只需用以下代码替换Yield仿函数Request:
factorial2 :: (Monad m) => Integer
-> Coroutine (Request Integer Integer) m Integer
factorial2 n = loop 1 n n
where
loop r t 0 = return r
loop r t n | t >= n = r' `par` request n >>= rec
| otherwise = rec t
where
rec t' = (r' `pseq` loop r') t' (n - 1)
r' = r * n
run2 :: IO ()
run2 = pogoStickM (\(Request i c) -> print i >> return (c (i - 5)))
(factorial2 30)
>>= print
Run Code Online (Sandbox Code Playgroud)
虽然我们的run...例子是IO基于的,但是阶乘的计算是纯粹的,它们允许任何单子(包括Identity).
仍然,使用Haskell的并行性,我们与报告代码并行运行纯计算(在从协程生成之前,我们创建一个计算乘法步骤的火花par).
也许最重要的是,这些类型确保协程不会行为不端.协同程序无法解决死锁问题 - 产生或请求反馈总是与适当的响应相结合(除非调用者决定不继续使用协程,在这种情况下它会被垃圾收集器自动删除,没有阻塞的线程) .