Если транзакция 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
. Но внесение этого улучшения лишает смысла задавать вопрос, поэтому я оставлю его здесь как есть.