一种在没有轮询的情况下在MVars上形成“选择”的方法

时间:2011-05-04 05:48:01

标签: haskell concurrency transactional-memory

我有两个MVar(一个MVar和一个Chan)。我需要从Chan中取出并处理它们,直到其他MVar不再为空。我理想的解决方案就像UNIX select函数,我传入一个(可能是空的)MVar列表和线程块直到其中一个已满,然后它返回完整的MVar。尽量尝试我可以想到除了用isEmptyMVar重复轮询每个MVar之前没办法做到这一点,直到我弄错。这似乎效率低下。

另一个想法是使用throwTo,但它会中断线程中发生的事情,我需要以原子方式完成处理Chan的工作。

我正在输入的最后一个想法是为每个MVar创建一个新的forkIO,它试图读取它的MVar然后用自己的实例填充一个新创建的MVar。然后原始线程可以阻止该MVar。 Haskell线程是否足够便宜以运行那么多?

2 个答案:

答案 0 :(得分:16)

Haskell线程非常便宜,所以你可以这样解决它,但听起来STM更适合你的问题。使用STM,你可以做到

do var <- atomically (takeTMVar a `orElse` takeTMVar b)
   ... do stuff with var

由于retryorElse的行为,此代码尝试获取a,如果失败,则获取b。如果两者都失败,则会阻塞,直到其中任何一个更新并再次尝试。

您甚至可以使用它来制作自己的select的基本版本:

select :: [TMVar a] -> STM a
select = foldr1 orElse . map takeTMVar

答案 1 :(得分:12)

使用TChanTVar行为的STM版本retryorElse怎么样?

实现select是STM的优秀功能之一。来自“可组合记忆交易”:

  

除此之外,我们还提供orElse,   这使得它们可以作为替代品组成   第二次运行如果第一次重试(第3.4节)。这种能力允许线程一次等待很多事情,比如   Unix选择系统调用 - 除了orElse编写得好,   而选择没有。