如果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
并在writeChan
if
atomically
enterShop
块{{1}}块中{{1}}后执行{{1}}。但是,做出这样的改进会破坏提出问题的全部理由,所以我会把它留在这里。
答案 0 :(得分:11)
TChan
操作,就像其他STM操作一样,因此无论您的事务重试多少次,您总会得到一次写操作。否则他们会变得毫无用处。
为了说服自己,试试这个例子:
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
这将抛出一个异常,抱怨该事务被无限期阻止。如果writeTChan
在事务提交之前导致写入,则程序将打印“hi!”在抛出异常之前。