我们在TChan上有一些倾销值,然后消费者处理这些值。但是消费者无法跟上,所以我们获得了大量的内存使用,因为制作人在频道上倾销了很多东西,但消费者却没有跟上。如果通道队列变成某个特定大小或某种东西,是否有一种直接的方法来生成生产者块,这样我们可以让生产者等待消费者赶上?
答案 0 :(得分:3)
就像John的回答一样,我建议你自己建立一个有界的TChan。我的代码不同,因为它:
BTChan
成为ADT)注意:老实说,如果我是你,我会忽略所有这些答案,只需要在他的评论中使用链接的代码(除非事实证明是错误的代码)。我敢打赌它和我在这里做的一样,但更多的想法。
{-# LANGUAGE BangPatterns #-}
module BTChan
( BTChan
, newBTChanIO
, newBTChan
, writeBTChan
, readBTChan
) where
import Control.Concurrent.STM
data BTChan a = BTChan {-# UNPACK #-} !Int (TChan a) (TVar Int)
-- | `newBTChan m` make a new bounded TChan of max size `m`
newBTChanIO :: Int -> IO (BTChan a)
newBTChanIO m = do
szTV <- newTVarIO 0
c <- newTChanIO
return (BTChan m c szTV)
newBTChan :: Int -> STM (BTChan a)
newBTChan m
| m < 1 = error "BTChan's can not have a maximum <= 0!"
| otherwise = do
szTV <- newTVar 0
c <- newTChan
return (BTChan m c szTV)
writeBTChan :: BTChan a -> a -> STM ()
writeBTChan (BTChan mx c szTV) x = do
sz <- readTVar szTV
if sz >= mx then retry else writeTVar szTV (sz + 1) >> writeTChan c x
readBTChan :: BTChan a -> STM a
readBTChan (BTChan _ c szTV) = do
x <- readTChan c
sz <- readTVar szTV
let !sz' = sz - 1
writeTVar szTV sz'
return x
sizeOfBTChan :: BTChan a -> STM Int
sizeOfBTChan (BTChan _ _ sTV) = readTVar sTV
STM程序员需要注意的一些事项:
retry
会产生,将您的haskell线程置于阻塞状态,等待TVar
或TChan
之一的状态更改,以便它可以重试。这是您避免在IO
中使用yield
函数检查值的方法。STVar
,STChan
,SBTChan
和BTChan
(严格和/或有界的TVars和TChans)的hackage包。newBTChanIO
而不是杠杆newBTChan
,因为new{TVar,TChan}IO
的实现即使在unsafePerformIO
下atomically
也可以使用{{1}}做不到。编辑:你可以通过将TVar分成一个用于读者和一个用于作者来实现性能提高2-5倍(取决于你使用的界限),从而减少争用。使用标准验证。改进版本0.2.1已经在讨价还价。
答案 1 :(得分:2)
最简单的解决方案可能是添加一个TVar
来表示频道中的元素数量:
type BoundedChan a = (TChan a, TVar Int, Int)
writeBoundedChan :: BoundedChan a -> a -> IO ()
writeBoundedChan bc@(tchan, tsz, maxsz) x = do
cursz' <- readTVarIO tsz
if cursz' >= maxsz
then yield >> writeBoundedChan bc x
else atomically $ do
writeTChan tchan a
cursz <- readTVar tsz
writeTVar tsz (cursz+1)
readBoundedChan :: BoundedChan a -> IO a
readBoundedChan (tchan, tsz, maxsz) = atomically $ do
x <- readTChan tchan
cursz <- readTVar tsz
writeTVar tsz (cursz-1)
return x
请注意,如果您有多个生成器,则可以稍微超出最大大小,因为cursz值可以在两次读取之间更改。
答案 2 :(得分:1)
我知道游戏有点晚了,但你可以选择实现一个跳过频道,它允许对频道进行非阻塞写入,但是“覆盖”没有的旧值。被任何读者都看到了。
import Control.Concurrent.MVar
data SkipChan a = SkipChan (MVar (a, [MVar ()])) (MVar ())
newSkipChan :: IO (SkipChan a)
newSkipChan = do
sem <- newEmptyMVar
main <- newMVar (undefined, [sem])
return (SkipChan main sem)
putSkipChan :: SkipChan a -> a -> IO ()
putSkipChan (SkipChan main _) v = do
(_, sems) <- takeMVar main
putMVar main (v, [])
mapM_ (\sem -> putMVar sem ()) sems
getSkipChan :: SkipChan a -> IO a
getSkipChan (SkipChan main sem) = do
takeMVar sem
(v, sems) <- takeMVar main
putMVar main (v, sem:sems)
return v
dupSkipChan :: SkipChan a -> IO (SkipChan a)
dupSkipChan (SkipChan main _) = do
sem <- newEmptyMVar
(v, sems) <- takeMVar main
putMVar main (v, sem:sems)
return (SkipChan main sem)
答案 3 :(得分:0)
hackage有BoundedChan,但它使用的是MVars,而不是STM。您可以使用它来学习如何编写自己的 - 它只是一页代码。