haskell - 管道和非管道代码之间的并发注意事项

标签 haskell api-design haskell-pipes

我正在为管道接口(interface)中的某些编码包装一个 C 库,但我遇到了一些需要做出的设计决策。

建立 C 库后,我们保留一个编码器上下文。有了这个,我们可以编码,或者改变一些参数(让我们调用 Haskell 接口(interface)到最后一个函数 tune :: Context -> Int -> IO () )。我的问题有两个部分:

  • 编码部分很容易包含在 Pipe Foo Bar IO () 中。 ,但我也想曝光tune .由于同时使用编码上下文必须受到锁保护,因此我需要在管道中的每次迭代时都锁定,并保护 tune使用相同的锁。但现在我觉得我在强制用户隐藏锁。我在这里吠错树了吗?这种情况在管道生态系统中通常是如何解决的?就我而言,我希望我的特定代码所属的管道始终在其自己的线程中运行,同时进行调整,但我不想将这种观点强加给任何用户。管道生态系统中的其他软件包似乎也没有强制他们的用户喜欢。
  • 不再使用的编码上下文需要正确地取消初始化。在管道生态系统中,如何确保在管道被破坏时处理这些事情(在这种情况下执行 som IO 操作)?

  • 一个具体的例子是包装一个压缩库,在这种情况下,上面可以是:
  • 压缩强度是可调的。我们设置了管道,它愉快地运行。假设必须序列化对压缩编解码器上下文的并发访问,最好如何在管道继续运行时更改压缩强度设置?
  • 压缩库在设置时从 Haskell 堆中分配了一堆内存,当管道被拆除时,我们需要调用一些库函数来清理这些内存。

  • 谢谢......这可能都很明显,但我对管道生态系统很陌生。

    编辑:发布后阅读此内容,我很确定这是我在这里问过的最模糊的问题。啊!对不起 ;-)

    最佳答案

    关于(1),一般的解决方案是改变你的Pipe的类型为:

    Pipe (Either (Context, Int) Foo) Bar IO ()
    

    换句话说,它同时接受 Foo输入和 tune请求,它在内部处理。

    那么让我们假设你有两个并发的Producer s 对应于输入和调整请求:
    producer1 :: Producer Foo IO ()
    
    producer2 :: Producer (Context, Int) IO ()
    

    您可以使用 pipes-concurrency创建一个它们都输入的缓冲区,如下所示:
    example = do
        (output, input) <- spawn Unbounded
        -- input  :: Input  (Either (Context, Int) Foo)
        -- output :: Output (Either (Context, Int) Foo)
    
        let io1 = runEffect $ producer1 >-> Pipes.Prelude.map Right >-> toOutput output
            io2 = runEffect $ producer2 >-> Pipes.Prelude.map Left  >-> toOutput output
        as <- mapM async [io1, io2]
        runEffect (fromInput >-> yourPipe >-> someConsumer)
        mapM_ wait as
    

    您可以了解更多关于pipes-concurrency图书馆阅读this tutorial .

    通过强制所有调整请求通过相同的单线程 Pipe您可以确保不会意外同时调用 tune功能。

    关于 (2) 有两种方法可以使用 pipes 获取资源.更复杂的方法是使用 pipes-safe库,它提供了 bracket可以在 Pipe 中使用的函数,但这对于您的目的来说可能是多余的,并且只存在于在管道的生命周期内获取和释放多个资源。更简单的解决方案是使用以下 with获取管道的成语:
    withEncoder :: (Pipe Foo Bar IO () -> IO r) -> IO r
    withEncoder k = bracket acquire release $ \resource -> do
        k (createPipeFromResource resource)
    

    然后用户会写:
    withEncoder $ \yourPipe -> do
        runEffect (someProducer >-> yourPipe >-> someConsumer)
    

    您可以选择使用 managed包,它稍微简化了类型,并且更容易获取多个资源。您可以通过阅读 this blog post of mine 了解更多信息.

    关于haskell - 管道和非管道代码之间的并发注意事项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25649213/

    相关文章:

    haskell - Cabal 安装后如何删除不需要的构建工件

    haskell - haskell在哪里保存状态?

    macos - 我需要一种无需在 Mac 上安装即可运行 Haskell 代码的方法

    java - 为什么 Collection<T> 不实现 Stream<T>?

    haskell Pipes - 如何在字节串管道上重复执行 takeWhile 操作?

    haskell - 带 Haskell 管道的有状态生成器

    haskell - Monad 转换器库 - 使用哪一个?

    oauth-2.0 - OAuth Server与Resource Server分离后如何处理用户注册?

    c++ - 当堆栈为空时,在(模板化的)堆栈弹出方法中做什么?

    haskell - 为什么 Conduit 和 Pipe 不能有 Arrow 实例?