如果 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 = 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
更新hairRequestChan
直到以上内容不一定是交易的一部分
之后我才注意到。我可以使用常规Chan
并在in块之后执行writeChan
inif
语句。但是做出这种改进会破坏提出这个问题的全部原因,所以我将保持原样。atomically
enterShop