Cercando di capire Haskell STM Simple Cose
-
28-10-2019 - |
Domanda
Mi sono bloccato nella comprensione del concetto di atomicamente in STM.
Illustro con un esempio
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import qualified Data.Map as Map
main :: IO ()
main = do
d <- atomically$ newTVar Map.empty
sockHandler d
sockHandler :: TVar (Map.Map String Int)-> IO ()
sockHandler d = do
forkIO $ commandProcessor d 1
forkIO $ commandProcessor d 2
forkIO $ commandProcessor d 3
forkIO $ commandProcessor d 4
forkIO (threadDelay 1000 >> putStrLn "Hello World?")
threadDelay 10000
return ()
commandProcessor :: TVar (Map.Map String Int)-> Int-> IO ()
commandProcessor d i= do
addCommand d i
commandProcessor d i
addCommand :: TVar (Map.Map String Int) ->Int -> IO ()
addCommand d i = do
succ <- atomically $ runAdd d
putStrLn $"Result of add in " ++ (show i)++ " " ++( show succ)
runAdd d =do
dl <- readTVar d
let (succ,g)= if Map.member "a" dl
then
(False,dl)
else
(True,Map.insert "a" 9 dl)
writeTVar d g
return succ
L'output del campione sarebbe così:
Risultato di Aggiungi in 1 vero risultato di Aggiungi in 4 falsi risultati di Aggiungi in 1 falseresult di Aggiungi in 2 falseresult di Add in 3 False Hello World? Risultato di Aggiungi in 4 falsi
Risultato dell'aggiunta in 1 falseresult di Aggiungi in 2 falsi risultato di Aggiungi in 3 falsi risultato di Aggiungi in 4 falsi
Risultato di Aggiungi in 1 falso risultato di Aggiungi in 2 falseresult di Aggiungi in 3 falseresult di Aggiungi in 4 falsi
Risultato di Aggiungi in 1 falso risultato di Aggiungi in 2 falseresult di Aggiungi in 3 falseresult di Aggiungi in 4 falsi
Risultato dell'aggiunta in 1 falso risultato di Aggiungi in 2 falseresult di Aggiungi in 4 falseresult di Aggiungi in 3 falsi
Risultato dell'aggiunta in 1 falso risultato di Aggiungi in 4 falseresult di Aggiungi in 2 falseresult di Aggiungi in 3 falsi
Risultato dell'aggiunta in 1 falseresult di Aggiungi in 4 falsi risultato di Aggiungi in 2 falsi risultato di Aggiungi in 3 falsi
Risultato dell'aggiunta in 1 falseresult di Aggiungi in 4 falsi
Risultato dell'aggiunta in 2 falseresult di Aggiungi in 3 falsi
Risultato dell'aggiunta in 1 falseresult di Aggiungi in 4 falsi
Risultato dell'aggiunta in 2 falseresult di Aggiungi in 3 falsi risultato di Aggiungi in 1 falso risultato di Aggiungi in 4 falsi
Risultato dell'aggiunta in 2 falseresult di Aggiungi in 3 falsi
Risultato dell'aggiunta in 1 falseresult di Aggiungi in 4 falsi
Quando leggo atomicamente
. Ciò significa che tutte le operazioni all'interno della transazione sono completamente complete, senza altri thread che modificano le variabili utilizzate dalla nostra transazione, o non riesce e lo stato viene riportato dove era prima dell'inizio della transazione. In breve, le transazioni atomiche sono complete completamente, o è come se non fossero mai gestite affatto.
Quindi alla domanda il "ritorno" di Succ in alcuni casi non accade mai? Cioè la linea SCET <- atomicamente $ runadd d putstrln $ "risultato dell'aggiunta" ++ (mostra i) ++ "" ++ (mostra succ)
Dai un output di "Risultato dell'aggiunta? I" ("come se non fossero mai gestiti")
Soluzione
Se una transazione viene ripostata, ciò che succede è che il tuo programma prova di nuovo. Puoi immaginare l'implementazione di atomically
Essere qualcosa del genere:
atomically action = do varState <- getStateOfTVars
(newState, ret) <- runTransactionWith action varState
success <- attemptToCommitChangesToTVars newState
if success
then return ret
else atomically action -- try again
Nel tuo caso, la transazione verrà sempre eseguita e completerà sempre. Può completare il secondo o il terzo tentativo a causa di conflitti, ma questo è invisibile per te, l'utente. STM si assicura che l'azione si verifichi atomicamente, anche se ci vuole alcuni prima che sia in grado di farlo con successo.
Altri suggerimenti
threadDelay
già ritorna (), non c'è bisogno di esplicitoreturn ()
in seguitonewTVarIO
è una versione concisa diatomically . newTVar
.- È più leggibile se usi
forever
Invece di chiamarti come fatticommandProcessor
.
Per quanto riguarda la tua domanda, la risposta è "sì". Si chiama Live-Lock in cui il tuo thread ha un lavoro da fare ma non può fare progressi. Immagina una funzione davvero costosa, expensive
, e una funzione davvero economica, cheap
. Se questi operano in competizione atomically
blocchi sullo stesso TVar
quindi la funzione economica può causare il file expensive
funzione da non completare mai. Ho costruito un esempio per un correlato quindi domanda.
Il tuo esempio di chiusura non è del tutto giusto, se l'operazione STM non è mai completata allora putStrLn
Non sarà mai raggiunto e non verrà visualizzata alcuna emissione da quel thread.