2013-06-05 12 views
8

Se una transazione STM fallisce e riprova, la chiamata a writeTChan viene rieseguita in modo da terminare con due scritture oppure STM esegue effettivamente la scrittura solo se la transazione viene eseguita? vale a dire, questa soluzione al problema del barbiere addormentato è valida oppure potrebbe un cliente ottenere due scarti se la transazione in enterShop non riesce la prima volta?Le scritture TChan sono integrate in Haskell STM?

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 non ho notato fino a dopo il fatto che quanto sopra hairRequestChan non deve essere parte della transazione in ogni caso. Posso usare un normale Chan e fare il writeChan in una dichiarazione ifdopo il il blocco atomically in enterShop. Ma fare in modo che questo miglioramento distrugga l'intera ragione per fare la domanda, quindi lo lascerò così com'è.

risposta

11

TChan le operazioni vengono eseguite quando si esegue il commit di una transazione, proprio come le altre operazioni STM, quindi si finisce sempre con una singola scrittura, indipendentemente dal numero di volte in cui la transazione viene ripetuta. Sarebbero altrimenti inutili.

Per convincersi, provare questo esempio:

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 

Questo lancerà un'eccezione lamentando che la transazione è bloccato a tempo indeterminato. Se writeTChan ha provocato una scrittura prima che la transazione venisse eseguita, il programma stamperebbe "hi!" prima di lanciare quell'eccezione.

+0

Grande esempio, grazie! –

+2

Infatti, i TChan sono implementati in puro Haskell usando TVars ([qui] (http://hackage.haskell.org/packages/archive/stm/2.1.2.2/doc/html/src/Control-Concurrent-STM-TChan. html) è la fonte del modulo TChan), quindi ricevono la stessa quantità di isolamento rispetto a TVars. – javawizard

Problemi correlati