TChan写入集成到Haskell STM? [英] Are TChan writes integrated into Haskell STM?
问题描述
如果STM事务失败并重试,那么对 writeTChan
的调用会被重新执行,以便最终执行两次写操作,还是STM实际上只执行写入事务是否提交?也就是说,对于睡眠理发师问题的解决方案是否有效,或者如果 enterShop
中的交易第一次失败,那么客户可能会获得两次理发吗?
import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
import System.Random
import Text.Printf
runBarber :: TChan Int - > TVar Int - > IO()
runBarber haircutRequestChan seatsLeftVar =永远$做
客户ID< - 原子$ readTChan haircutRequestChan
原子$做
seatsLeft< - readTVar seatsLeftVar
writeTVar的seatsLeftVar $ seatLeft + 1
putStrLn $ printf%d开始切割customerId
延迟< - randomRIO(1,700)
threadDelay延迟
putStrLn $ printf%d完成切割customerId
enterShop :: TChan Int - > TVar Int - > Int - > IO()
enterShop haircutRequestChan seatsLeftVar客户ID =做
putStrLn $ printf的 %d进入商店 客户ID
hasEmptySeat< - 原子$做
seatsLeft< - readTVar seatsLeftVar
let hasEmptySeat = seatsLeft> 0
时hasEmptySeat $ DO
writeTVar的seatsLeftVar $ seatsLeft - 1个
writeTChan haircutRequestChan客户ID
返回hasEmptySeat
时(不hasEmptySeat)$做
putStrLn $的printf %d拒之门外 客户ID
主要=做
seatsLeftVar< - newTVarIO 3
haircutRequestChan< - newTChanIO
forkIO $ runBarber haircutRequestChan seatsLeftVar
forM_ [1..20] $ \customerId - >
delay< - randomRIO(1,3)
threadDelay延迟
forkIO $ enterShop haircutRequestChan seatsLeftVar customerId
更新
直到事实发现上面的 hairRequestChan
doesn'无论如何,都必须成为交易的一部分。如果中可以使用常规 Chan
并执行 writeChan
>在 enterShop
中 原子地块之后的
>import Control.Concurrent
import Control.Concurrent.STM
import Control.Concurrent.STM.TChan
main = do
ch< - 自动newTChan
forkIO $阅读器ch>> = putStrLn
作家ch
阅读器=自动。 readTChan
编写器ch =原子状态$ writeTChan chhi! >>重试
这会引发一个异常,抱怨事务被无限期阻塞。如果在提交事务之前 writeTChan
导致发生写入,程序将打印hi!然后抛出该异常。
If an STM transaction fails and retries, does the call to writeTChan
get re-executed so that you end up with two writes, or does the STM only actually perform the write if the transaction commits? i.e., is this solution to the sleeping barber problem valid, or might a customer get two haircuts if the transaction in enterShop
fails the first time?
import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
import System.Random
import Text.Printf
runBarber :: TChan Int -> TVar Int -> IO ()
runBarber haircutRequestChan seatsLeftVar = forever $ do
customerId <- atomically $ readTChan haircutRequestChan
atomically $ do
seatsLeft <- readTVar seatsLeftVar
writeTVar seatsLeftVar $ seatsLeft + 1
putStrLn $ printf "%d started cutting" customerId
delay <- randomRIO (1,700)
threadDelay delay
putStrLn $ printf "%d finished cutting" customerId
enterShop :: TChan Int -> TVar Int -> Int -> IO ()
enterShop haircutRequestChan seatsLeftVar customerId = do
putStrLn $ printf "%d entering shop" customerId
hasEmptySeat <- atomically $ do
seatsLeft <- readTVar seatsLeftVar
let hasEmptySeat = seatsLeft > 0
when hasEmptySeat $ do
writeTVar seatsLeftVar $ seatsLeft - 1
writeTChan haircutRequestChan customerId
return hasEmptySeat
when (not hasEmptySeat) $ do
putStrLn $ printf "%d turned away" customerId
main = do
seatsLeftVar <- newTVarIO 3
haircutRequestChan <- newTChanIO
forkIO $ runBarber haircutRequestChan seatsLeftVar
forM_ [1..20] $ \customerId -> do
delay <- randomRIO (1,3)
threadDelay delay
forkIO $ enterShop haircutRequestChan seatsLeftVar customerId
UPDATE
I didn't notice until after the fact that the above hairRequestChan
doesn't have to be part of the transaction anyway. I can use a regular Chan
and do the writeChan
in an if
statement after the atomically
block in enterShop
. But making that improvement destroys the whole reason for asking the question, so I'll leave it as-is here.
TChan
operations are performed when a transaction is committed, just like other STM operations, so you'll always end up with a single write, no matter how many times your transaction is retried. They'd be kind of useless otherwise.
To convince yourself, try this example:
import Control.Concurrent
import Control.Concurrent.STM
import Control.Concurrent.STM.TChan
main = do
ch <- atomically newTChan
forkIO $ reader ch >>= putStrLn
writer ch
reader = atomically . readTChan
writer ch = atomically $ writeTChan ch "hi!" >> retry
This will throw a exception complaining that the transaction is blocked indefinitely. If writeTChan
caused a write to happen before the transaction was committed, the program would print "hi!" before throwing that exception.
这篇关于TChan写入集成到Haskell STM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!