2011-08-31 16 views
8

sto cercando di capire l'esempio MVAR nel GHC latest docs -Aiuto comprensione esempio MVAR in Haskell

data SkipChan a = SkipChan (MVar (a, [MVar()])) (MVar()) 

newSkipChan :: IO (SkipChan a) 
newSkipChan = do 
    sem <- newEmptyMVar 
    main <- newMVar (undefined, [sem]) 
    return (SkipChan main sem) 

putSkipChan :: SkipChan a -> a -> IO() 
putSkipChan (SkipChan main _) v = do 
    (_, sems) <- takeMVar main 
    putMVar main (v, []) 
    mapM_ (sem -> putMVar sem()) sems 

getSkipChan :: SkipChan a -> IO a 
getSkipChan (SkipChan main sem) = do 
    takeMVar sem 
    (v, sems) <- takeMVar main 
    putMVar main (v, sem:sems) 
    return v 

dupSkipChan :: SkipChan a -> IO (SkipChan a) 
dupSkipChan (SkipChan main _) = do 
    sem <- newEmptyMVar 
    (v, sems) <- takeMVar main 
    putMVar main (v, sem:sems) 
    return (SkipChan main sem) 

Capisco la maggior parte del programma, ma per due domande -

  1. sono operazioni come putSkipChan atomico? Sembra di evitare il blocco su putMVar facendo prima un takeMVar. Ma non fallirebbe se qualcos'altro chiama putMVar dopo il takeMVar ma prima dello putMVar? In questi casi, sembra che il programma bloccherà per sempre.
  2. Perché dupSkipChan aggiungi sem all'elenco di semafori nello SkipChan? Non è quello fatto da getSkipChan. Mi sembra che chiamare dupSkipChan seguito da getSkipChan (che sembra essere quello che hai avere da fare per avere più lettori) causerebbe un blocco quando putSkipChan tenta di risvegliare lo stesso semaforo due volte?

risposta

5
  1. Lei ha ragione, un altro thread potrebbe chiamare putMVar main e rovinare putSkipChan. Ma il modulo che crea il codice sopra non esporterebbe il costruttore SkipChan in modo tale operazione impossibile sarebbe impossibile.

  2. dupSkipChan fa un nuovaemptyMVar chiamato sem e aggiunge che nella lista in principale. Non aggiunge quello preesistente che è stato creato in newSkipChan. Quindi non c'è un blocco.

Per spiegare di più ad altri lettori di questa domanda e commento: L'idea è che potrebbero esserci più thread di lettura. Inizialmente SkipChan main sem1 è l'unico lettore di questo tipo. dupSkipChan fa un SkipChan main sem2. Se ci sono migliaia di lettori, non si vorrebbe avvisare tutti di un nuovo valore in putSkipChan, quindi il motivo è che getSkipChan inserisce il suo sem nella lista principale. L'inizializzazione di SkipChan come fatto in newSkipChan e dupSkipChan include anche il mettere il nuovo sem vuoto nella lista principale.

L'inizializzazione e il design sopra riportati indicano che il primo getSkipChan ottiene il valore passato più recente che è stato scritto (o il blocco per il primo valore che deve arrivare). Il futuro getSkipChan su quello SkipChan otterrà sempre un valore più nuovo rispetto a qualsiasi altro ottenuto prima e questi non verranno bloccati se tale valore è già disponibile.

Problemi correlati