2012-08-30 4 views
5

Afin de se familiariser avec la STM en Haskell, j'ai écrit la solution suivante à la salle à Philosophes problème:Qu'est-ce qui ne va pas avec la solution suivante aux "Philosophes à manger"?

import Control.Concurrent 
import Control.Concurrent.STM 
import Control.Monad 
import System.Random 

type Fork = TVar Bool 
type StringBuffer = TChan String 

philosopherNames :: [String] 
philosopherNames = map show ([1..] :: [Int]) 

logThinking :: String -> StringBuffer -> STM() 
logThinking name buffer = writeTChan buffer $ name ++ " is thinking..." 

logEating :: String -> StringBuffer -> STM() 
logEating name buffer = writeTChan buffer $ name ++ " is eating..." 

firstLogEntry :: StringBuffer -> STM String 
firstLogEntry buffer = do empty <- isEmptyTChan buffer 
          if empty then retry 
            else readTChan buffer 

takeForks :: Fork -> Fork -> STM() 
takeForks left right = do leftUsed <- readTVar left 
          rightUsed <- readTVar right 
          if leftUsed || rightUsed 
          then retry 
          else do writeTVar left True 
            writeTVar right True 

putForks :: Fork -> Fork -> STM() 
putForks left right = do writeTVar left False 
         writeTVar right False 

philosopher :: String -> StringBuffer -> Fork -> Fork -> IO() 
philosopher name out left right = do atomically $ logThinking name out 
            randomDelay 
            atomically $ takeForks left right 
            atomically $ logEating name out 
            randomDelay 
            atomically $ putForks left right 

randomDelay :: IO() 
randomDelay = do delay <- getStdRandom(randomR (1,3)) 
       threadDelay (delay * 1000000) 

main :: IO() 
main = do let n = 8 
      forks <- replicateM n $ newTVarIO False 
      buffer <- newTChanIO 
      forM_ [0 .. n - 1] $ \i -> 
       do let left = forks !! i 
        right = forks !! ((i + 1) `mod` n) 
        name = philosopherNames !! i 
       forkIO $ forever $ philosopher name buffer left right 

      forever $ do str <- atomically $ firstLogEntry buffer 
         putStrLn str 

Quand je compile et exécute ma solution, il semble que pas de problèmes de concurrence évidentes existent: Chaque philosophe finalement manger et aucun philosophe ne semble être favorisé. Cependant, si je retire les déclarations de randomDelayphilosopher, compiler et exécuter, la sortie de mon programme se présente comme suit:

1 is thinking... 
1 is eating... 
1 is thinking... 
1 is eating... 
2 is thinking... 
2 is eating... 
2 is thinking... 
2 is eating... 
2 is thinking... 
2 is eating... 
2 is thinking... 

About 2500 lines later... 

2 is thinking... 
2 is eating... 
2 is thinking... 
3 is thinking... 
3 is eating... 
3 is thinking... 
3 is eating... 

And so on... 

ce qui se passe dans ce cas?

+0

S'il s'agit de devoirs, veuillez ajouter l'onglet des devoirs. – Gray

+0

Ce n'est pas à la maison, je lis à propos de STM dans Real World Haskell et j'essaie de me familiariser avec ça. – Alexandros

+0

avec ma configuration (Test Debian 6, ghc 7.4.1, runhaskell/ghc -O2 --make philosophers.hs) Je ne pense pas avoir de problème - les philosophes 1 ..8 mangent et pensent chacun à son tour. Quelle est votre version de ghc et compilez-vous ou utilisez-vous ghci? – epsilonhalbe

Répondre

5

Vous devez compiler avec le moteur d'exécution fileté et activé rtsopts, et l'exécuter avec +RTS -N (ou +RTS -Nkk est le nombre de threads. Avec cela, je reçois une sortie comme

8 is eating... 
6 is eating... 
4 is thinking... 
6 is thinking... 
4 is eating... 
7 is eating... 
8 is thinking... 
4 is thinking... 
7 is thinking... 
8 is eating... 
4 is eating... 
4 is thinking... 
4 is eating... 
6 is eating... 
4 is thinking... 

Le point est que pour qu'un autre philosophe pense/mange, un changement de contexte doit avoir lieu si vous n'avez pas plusieurs threads matériels à votre disposition, un tel changement de contexte ne se produit pas souvent ici, où peu d'allocation est faite, donc chaque philosophe a beaucoup de temps pour réfléchir et manger beaucoup avant que le tour suivant ne se produise:

Avec assez de threads à votre disposition, tous les philosophes peuvent simultanément tenter d'atteindre les fourchettes.

+0

Avec '+ RTS -N9' (8 threads pour chaque philosophe et 1 pour le thread principal, qui écrit à' stdout'), il semble que deux philosophes monopolisent le CPU pendant un certain temps. – Alexandros

+4

Combien de noyaux avez-vous? Il ne peut y avoir plus de threads en cours d'exécution que de capacités matérielles, donc si vous avez une machine dual core, pas plus de deux philosophes peuvent rivaliser pour les fourches à tout moment. –

+0

Il vaut mieux considérer '-Nk' comme contrôlant le nombre de cœurs à utiliser plutôt que le nombre de threads du système d'exploitation. Dans d'autres cas, cela est important si vous utilisez 'forkOS' ou effectuez des appels FFI. –

Questions connexes