2012-08-30 7 views
5

Al fine di acquisire familiarità con STM in Haskell, ho scritto la seguente soluzione al problema è possibile pranzare e Filosofi:Cosa c'è di sbagliato nella seguente soluzione per i "Dining Philosophers"?

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 

Quando compilo e corro la mia soluzione, sembra che nessun problema di concorrenza evidenti esistere: Ogni filosofo alla fine mangi e nessun filosofo sembra essere favorito. Tuttavia, se mi tolgo la randomDelay dichiarazioni da philosopher, compilare ed eseguire, l'uscita del mio programma è simile al seguente:

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... 

ciò che sta accadendo in questo caso?

+0

Se questo è compito, si prega di aggiungere la scheda compiti a casa. – Gray

+0

Non è compito, ho letto di STM in Real World Haskell e sto cercando di familiarizzare con esso. – Alexandros

+0

con il mio setup (Debian 6 Testing, ghc 7.4.1, runhaskell/ghc -O2 --make philosophers.hs) Non penso di avere un problema - i filosofi 1 ..8 stanno mangiando e pensando ognuno al suo turno. Qual è la tua versione ghc e stai compilando o usando ghci? – epsilonhalbe

risposta

5

È necessario compilare con il runtime filettato e abilitato rtsopts, ed esegue con +RTS -N (o +RTS -Nk dove k è il numero di fili. Con questo, ottengo output come

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... 

Il punto è che per un altro filosofo pensare/mangiare, un interruttore di contesto deve accadere se non si dispone di diversi thread hardware a propria disposizione.Questo switch di contesto non si verifica molto spesso qui, dove non viene eseguita molta allocazione, quindi ogni filosofo ha un sacco di tempo per pensare e mangiare molto prima che arrivi il prossimo turno.

Con abbastanza fili a disposizione, tutti i filosofi possono cercare contemporaneamente di raggiungere le forche.

+0

Con '+ RTS -N9' (8 thread per ogni filosofo e 1 per il thread principale, che scrive su' stdout'), sembra che due filosofi monopolizzino la CPU per un determinato periodo di tempo. – Alexandros

+4

Quanti core hai? Non ci possono essere più thread in esecuzione nello stesso momento di quanto non si abbiano capacità hardware, quindi se si dispone di una macchina dual core, non più di due filosofi possono competere per le forcelle in qualsiasi momento. –

+0

È meglio pensare a '-Nk' come a controllare il numero di core da utilizzare piuttosto che il numero di thread del sistema operativo. Tra gli altri casi, questo è importante se si utilizza 'forkOS' o si effettuano chiamate FFI. –

Problemi correlati