Помогите понять пример MVar в Haskell - PullRequest
8 голосов
/ 31 августа 2011

Я пытаюсь понять пример MVar в последних документах GHC -

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)

Я понимаю большую часть программы, но для двух вопросов -

  1. Являются ли операции типа putSkipChan атомарными?Кажется, чтобы избежать блокировки на putMVar, сначала выполните takeMVar.Но разве это не сработает, если что-то еще вызовет putMVar после takeMVar, но до putMVar?В таких случаях кажется, что программа будет блокироваться навсегда.
  2. Почему dupSkipChan добавляет sem к списку семафоров в SkipChan?Разве это не сделано getSkipChan.Мне кажется, что вызов dupSkipChan с последующим getSkipChan (что, похоже, должен сделать , чтобы иметь несколько считывателей) вызовет блок, когда putSkipChan попытается разбудить тот же семафордважды?

1 Ответ

5 голосов
/ 31 августа 2011
  1. Вы правы, другой поток может вызвать putMVar main и испортить putSkipChan.Но модуль, создающий вышеуказанный код, не будет экспортировать конструктор SkipChan, поэтому такая мошенническая операция будет невозможна.

  2. dupSkipChan делает new emptyMVar называется sem и добавляет это в список в основном.Он не добавляет ранее существовавший, созданный в newSkipChan.Таким образом, здесь нет блока.

Чтобы объяснить больше другим читателям этого вопроса и комментария: Идея состоит в том, что может быть несколько потоков читателей.Изначально SkipChan main sem1 является единственным таким читателем.dupSkipChan составляет SkipChan main sem2.Если есть тысячи читателей, то вы не захотите уведомлять всех их о новом значении в putSkipChan, поэтому конструкция такова, что getSkipChan помещает свою семью в список main.Инициализация SkipChan, как это делается в newSkipChan и dupSkipChan, также включает добавление нового пустого sem в список main.

Вышеуказанная инициализация и дизайн означают, что первый getSkipChan получает наибольшеепоследнее прошедшее значение было записано (или заблокировано для получения первого значения).Будущее getSkipChan для этого SkipChan всегда получит более новое значение, чем любое, полученное ранее, и не будет блокироваться, если это значение уже доступно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...