Essayer de comprendre les choses simples Haskell STM
-
28-10-2019 - |
Question
Je me suis coincé dans la compréhension du concept de atomiquement dans STM.
J'illustre avec un exemple
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
La sortie de l'échantillon serait comme ceci:
Résultat d'ajouter dans 1 Vrai Résultat d'add dans 4 Résultat Faux d'ajouter dans 1 FalseResult d'ajouter dans 2 FalseResult de ajouter 3 Faux Bonjour tout le monde? Résultat de ajouter 4 Faux
Résultat d'ajouter à 1 FalseResult d'ajouter dans 2 Résultat Faux d'ajouter dans 3 Faux Résultat d'ajouter dans 4 False
Résultat d'ajouter dans 1 Résultat Faux d'ajouter dans 2 FalseResult d'ajouter dans 3 FalseResult d'ajouter dans 4 Faux
Résultat d'ajouter dans 1 Résultat Faux d'ajouter dans 2 FalseResult d'ajouter dans 3 FalseResult d'ajouter dans 4 Faux
Résultat d'ajouter dans 1 Résultat Faux d'ajouter dans 2 FalseResult d'ajouter à 4 FalseResult d'ajouter dans 3 Faux
Résultat d'ajouter dans 1 Résultat Faux d'ajouter en 4 FalseResult d'ajouter dans 2 FalseResult d'ajouter dans 3 Faux
Résultat d'ajouter à 1 FalseResult d'ajouter dans 4 Résultat Faux d'ajouter dans 2 Faux Résultat d'ajouter dans 3 Faux
Résultat d'ajouter à 1 FalseResult d'ajouter en 4 Faux
Résultat d'ajouter dans 2 FalseResult des add dans 3 Faux
Résultat d'ajouter à 1 FalseResult d'ajouter en 4 Faux
Résultat d'ajouter dans 2 FalseResult des add dans 3 Résultat Faux d'ajouter dans 1 Faux Résultat d'ajouter dans 4 False
Résultat d'ajouter dans 2 FalseResult des add dans 3 Faux
Résultat d'ajouter à 1 FalseResult d'ajouter en 4 Faux
Quand je lis à propos atomiquement
. Cela signifie que toutes les opérations à l'intérieur de la transaction entièrement complète, sans aucun autre fils modifier les variables que notre transaction utilise, ou il tombe en panne, et l'état est annulée à l'endroit où il était avant que la transaction a commencé. En bref, les transactions atomiques soit complète entièrement, ou il est comme si elles ne fonctionnent pas du tout.
Donc la question pourrait le « retour » de succ dans certains cas, ne jamais se produire? C'est possible la ligne succ <- atomiquement runAdd $ d putStrLn $ "Résultat d'ajouter dans" ++ (montrer i) ++ "" ++ (succ show)
donner une sortie de « Résultat d'ajouter à? I » ( « comme si elles ne manquent jamais du tout »)
La solution
Si une transaction ne soit annulée, ce qui se passe est que votre programme essaie à nouveau. Vous pouvez imaginer la mise en œuvre de atomically
être quelque chose comme ceci:
atomically action = do varState <- getStateOfTVars
(newState, ret) <- runTransactionWith action varState
success <- attemptToCommitChangesToTVars newState
if success
then return ret
else atomically action -- try again
Dans votre cas, la transaction sera toujours courir et sera toujours complète. Il peut compléter le deuxième ou troisième tentative en raison de conflits, mais que vous est invisible pour l'utilisateur. STM veille à ce que l'action se atomiquement, même si cela prend quelques essais avant qu'il ne soit en mesure de le faire avec succès.
Autres conseils
-
threadDelay
déjà des retours (), pas besoin de explicitementreturn ()
après -
newTVarIO
est une version concise deatomically . newTVar
. - Il est plus facile à lire si vous utilisez
forever
au lieu de la queue vous appeler comme cela se fait danscommandProcessor
.
Quant à votre question, la réponse est « oui ». Il est appelé verrou en direct dans lequel votre fil a du travail à faire, mais il ne peut pas faire des progrès. Imaginez une fonction vraiment cher, expensive
, et une fonction vraiment pas cher, cheap
. Si ceux-ci fonctionnent en concurrence des blocs de atomically
sur la même TVar
alors la fonction peut entraîner la pas cher fonction expensive
de ne jamais complète. Je construit un exemple pour un lié ??SO question .
Votre exemple de fermeture n'est pas tout à fait raison que, si l'opération STM ne se termine jamais alors putStrLn
ne sera jamais atteint et aucune sortie visible du tout de ce fil.