2011-08-31 9 views
8

Próbuję zrozumieć przykład MVar w GHC latest docs -Pomoc zrozumienia MVar przykład w 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) 

rozumiem większość programu, ale na dwa pytania -

  1. to operacje takie jak putSkipChan atomowy? Wygląda na to, że unikniesz blokowania na putMVar, wykonując najpierw takeMVar. Ale czy to nie zawiedzie, jeśli coś innego zadzwoni pod numer putMVar po takeMVar, ale przed putMVar? W takich przypadkach wygląda na to, że program zostanie zablokowany na zawsze.
  2. Dlaczego dupSkipChan dołączyć sem do listy semaforów w SkipChan? Czy to nie jest robione przez getSkipChan. Wydaje mi się, że wywołanie dupSkipChan, a następnie getSkipChan (która wydaje się być tym, co ma zrobić, aby mieć wiele czytników) spowodowałoby blok, gdy putSkipChan próbuje obudzić ten sam semafor dwa razy?

Odpowiedz

5
  1. Masz rację, inny wątek mógł zadzwonić putMVar main i bałagan putSkipChan. Ale moduł tworzący powyższy kod nie eksportowałby konstruktora SkipChan, więc taka nieuczciwa operacja byłaby niemożliwa.

  2. dupSkipChan sprawia nowyemptyMVar nazwie sem i dodaje, że na liście w głównym. Nie dodaje wcześniej istniejącego, który został utworzony w newSkipChan. Tak więc nie ma bloku.

Aby wyjaśnić więcej innym czytelnikom tego pytania i komentarza: Chodzi o to, że mogą istnieć wątki wielu czytników. Początkowo SkipChan main sem1 jest jedynym takim czytnikiem. dupSkipChan tworzy SkipChan main sem2. Jeśli istnieją tysiące czytników, nie chcesz powiadamiać ich o nowej wartości w putSkipChan, więc projekt jest taki, że getSkipChan umieszcza swój sem na liście w main. Inicjowanie SkipChan jak w newSkipChan i dupSkipChan obejmuje także umieszczanie nowego pustego sem na liście w głównej.

Powyższa inicjalizacja i projektowanie oznaczają, że pierwsza wersja getSkipChan uzyskuje ostatnią zapisaną wartość (lub blokującą pierwszą wartość, która ma nadejść). Przyszłość getSkipChan na tej SkipChan zawsze otrzyma nowszą wartość niż jakakolwiek wcześniej zdobyta, i nie będzie blokować, jeśli ta wartość jest już dostępna.