我正在为管道界面中的某些编码包装C库,但我已经做了一些需要做出的设计决策。
在设置C库之后,我们继续编码器上下文。有了这个,我们可以编码或更改一些参数(让我们将Haskell接口调用到最后一个函数tune :: Context -> Int -> IO ()
)。我的问题分为两部分:
Pipe Foo Bar IO ()
中,但我还希望公开tune
。由于编码上下文的同时使用必须受到锁定保护,因此我需要在管道中的每次迭代时锁定,并使用相同的锁保护tune
。但现在我觉得我正在强迫用户隐藏锁定。我在这里吠叫错了吗?这种情况通常如何在管道生态系统中得到解决?在 my 的情况下,我希望我的特定代码所属的管道始终在自己的线程中运行,同时进行调优,但我不想强迫任何用户使用这种观点。管道生态系统中的其他软件包似乎也不会强迫用户使用。IO
操作)?一个具体的例子是包装压缩库,在这种情况下,上面可以是:
谢谢......这可能都很明显,但我对管道生态系统还很陌生。
编辑:在发帖后阅读此内容,我很确定这是我在这里问过的最模糊的问题。啊!对不起; - )
答案 0 :(得分:4)
关于(1),一般的解决方案是将Pipe
的类型更改为:
Pipe (Either (Context, Int) Foo) Bar IO ()
换句话说,它接受Foo
输入和tune
请求,并在内部处理。
那么让我们假设您有两个对应于输入和调优请求的并发Producer
:
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
通过阅读this tutorial,您可以了解有关pipes-concurrency
库的更多信息。
通过强制所有调优请求通过相同的单线程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了解更多信息。