2014-09-03 29 views
5

我正在为管道接口中的某些编码封装一个C库的过程,但我已经遇到了一些需要做出的设计决策。管道和非管道代码之间的并发注意事项

C库设置完成后,我们保持编码器上下文。有了这个,我们可以编码或更改一些参数(让我们将Haskell接口称为最后一个函数tune :: Context -> Int -> IO())。有两个部分,以我的问题:

  1. 编码部分容易在Pipe Foo Bar IO()包裹起来,但我也想揭露tune。由于编码上下文的同时使用必须锁定保护,因此需要在管道中的每次迭代中锁定一个锁,并使用相同的锁来保护tune。但现在我觉得我强迫用户隐藏锁。我在这里咆哮错误的树吗?这种情况在管道生态系统中通常如何解决?在我的情况下,我期望我的特定代码所属的管道始终在其自己的线程中运行,并发调整同时发生,但我不想强制任何用户的这种观点。管道生态系统中的其他软件包似乎并不像他们那样强迫用户。
  2. 不再使用的编码上下文需要被正确地初始化。在管道生态系统中,如何确保在管道被销毁时管理这些事情(在这种情况下执行som IO动作)?

的具体例子将被缠绕的压缩库,在这种情况下上面的可以是:

  1. 压缩强度是可调的。我们建立了管道,并快速奔跑。假设对压缩编解码器上下文的并发访问必须被序列化,那么在管道保持运行时允许更改压缩强度设置的最佳方法是什么?
  2. 压缩库在设置时从Haskell堆中分配了一堆内存,当管道被拆除时,我们需要调用一些库函数来清除它。

谢谢......这可能都是显而易见的,但我对管道生态系统相当陌生。

编辑:在发布后阅读本文,我很肯定这是我在这里问过的最隐秘的问题。啊!遗憾;-)

+3

你检查过'pipes-concurrency'库吗?它有一个[tutorial module](http://hackage.haskell.org/package/pipes-concurrency-2.0.2/docs/Pipes-Concurrent-Tutorial.html),您可能会发现它很有用。今晚晚些时候我会给出这个答案。 – 2014-09-03 16:45:37

+0

哦,我完全忽略了管道 - 并发性!谢谢,先生。管家伙!我仍然不确定它是否完全覆盖我的案例,但我会研究它。 – gspr 2014-09-03 17:14:14

+0

@gspr单个管道可以更改“压缩强度”参数,还是只能读取它?所有管道必须共享相同的参数吗? – danidiaz 2014-09-03 19:11:59

回答

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了解更多信息。

+0

太棒了!你通过我的混乱描述清楚地了解我的问题! – gspr 2014-09-07 07:13:57

+0

这是因为我以前有过这个完全相同的问题! :) – 2014-09-07 19:55:02