gpt4 book ai didi

multithreading - Haskell 中的 forkIO 和协程

转载 作者:行者123 更新时间:2023-12-04 17:09:16 25 4
gpt4 key购买 nike

我试图了解协程,但考虑到 forkIO 线程的存在,我不太了解它们的目的。哪些用例需要在线程上使用协程?

最佳答案

如果您在谈论特定的 Haskell 协程实现(如果是,请添加链接),或者关于一般概念,您的问题有点不清楚。

使用 forkIO并且某种线程间通信是实现协程的一种方式。这样做的好处是您可以利用拥有多个 CPU/内核的优势,但在我看来,有几个缺点:

  • 显式并发IO基于,所以你所有的计算都必须在 IO 中运行单子(monad)。
  • 您必须显式实现线程间通信。
  • 您必须注意启动线程,更重要的是,处理它们并防止饥饿/死锁。
  • 该架构(显然)是多线程的。在某些情况下,这可能是一个缺点。例如,您可能希望您的计算是纯粹的、确定性的、单线程的,但仍然使用协程的概念。

  • 我会进一步假设您的问题是关于 this 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

    现在假设我们意识到每个周期后的让步效率太低了。相反,我们希望调用者在再次挂起之前决定我们应该计算多少个周期。为此,我们只需替换 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...示例是 IO基于 - 的阶乘计算是纯的,它们允许任何单子(monad)(包括 Identity )。

    尽管如此,使用 Haskell's parallelism我们与报告代码并行运行纯计算(在从协程产生之前,我们创建了一个使用 par 计算乘法步骤的 Spark )。

    也许最重要的是,这些类型确保协程不会行为不端。协程不可能死锁 - 产生或请求反馈总是与适当的响应相结合(除非调用者决定不继续使用协程,在这种情况下它会被垃圾收集器自动删除,没有阻塞的线程) .

    关于multithreading - Haskell 中的 forkIO 和协程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27656073/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com