我正在为管道接口(interface)中的某些编码包装一个 C 库,但我遇到了一些需要做出的设计决策。
建立 C 库后,我们保留一个编码器上下文。有了这个,我们可以编码,或者改变一些参数(让我们调用 Haskell 接口(interface)到最后一个函数 tune :: Context -> Int -> IO ()
)。我的问题有两个部分:
Pipe Foo Bar IO ()
中。 ,但我也想曝光tune
.由于同时使用编码上下文必须受到锁保护,因此我需要在管道中的每次迭代时都锁定,并保护 tune
使用相同的锁。但现在我觉得我在强制用户隐藏锁。我在这里吠错树了吗?这种情况在管道生态系统中通常是如何解决的?就我而言,我希望我的特定代码所属的管道始终在其自己的线程中运行,同时进行调整,但我不想将这种观点强加给任何用户。管道生态系统中的其他软件包似乎也没有强制他们的用户喜欢。 IO
操作)? 一个具体的例子是包装一个压缩库,在这种情况下,上面可以是:
谢谢......这可能都很明显,但我对管道生态系统很陌生。
编辑:发布后阅读此内容,我很确定这是我在这里问过的最模糊的问题。啊!对不起 ;-)
最佳答案
关于(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/