Spec-Zone.ru › Haskell 9

Control.Concurrent.MVar

Авторские права (с) Университет Глазго 2001
Лицензия BSD-стиль (см. файл libraries/base/LICENSE)
Поддерживающий libraries@haskell.org
Стабильность стабильная
Переносимость непереносимая (конкурентность)
Безопасный Haskell Безопасный
Язык Haskell2010

Содержание

  • MVars

Описание

MVar t — это изменяемая область памяти, которая либо пуста, либо содержит значение типа t. Она имеет две основные операции: putMVar, которая заполняет MVar если он пуст и блокирует в противном случае, и takeMVar, которая очищает MVar если он заполнен и блокирует в противном случае. Их можно использовать различными способами:

  1. В качестве синхронизированных изменяемых переменных,
  2. В качестве каналов, с takeMVar и putMVar в качестве приёма и отправки, и
  3. В качестве двоичного семафора MVar (), с takeMVar и putMVar в качестве ожидания и сигнала.

Они были представлены в статье "Concurrent Haskell" Саймона Пейтона Джонса, Эндрю Гордона и Сигбьёрна Финна, хотя некоторые детали их реализации с тех пор изменились (в частности, запись в заполненный MVar раньше вызывала ошибку, но теперь просто блокирует.)

Применимость

MVar предлагают больше гибкости, чем IORef, но меньше гибкости, чем STM. Они подходят для создания примитивов синхронизации и выполнения простого межпоточного обмена данными; однако они очень просты и подвержены гонкам, тупикам или необработанным исключениям. Не используйте их, если вам нужно выполнить более сложные атомарные операции, такие как чтение из нескольких переменных: используйте STM вместо этого.

В частности, «более крупные» функции в этом модуле (swapMVar, withMVar, modifyMVar_ и modifyMVar) — это просто композиция takeMVar и putMVar с безопасностью исключений. Эти функции гарантируют атомарность только если все другие потоки выполняют takeMVar перед putMVar; в противном случае они могут заблокироваться.

Справедливость

Ни один поток не может быть заблокирован неопределённо долго на MVar, если другой поток не удерживает это MVar неопределённо долго. Одно из обычных решений для обеспечения этой гарантии справедливости заключается в том, что потоки, заблокированные на MVar, обслуживаются по принципу «первый пришёл — первый обслужен» (так делает GHC), но это не гарантируется в семантике.

Особенности

Как и многие другие структуры данных Haskell, MVar являются ленивыми. Это означает, что если вы поместите дорогостоящую невычисленную ленивую вычислительную структуру (thunk) в MVar, она будет вычислена потоком, который её потребляет, а не потоком, который её создал. Убедитесь, что вы evaluate значения, которые нужно поместить в MVar, в соответствующую нормальную форму, или воспользуйтесь строгой MVar из пакета strict-concurrency.

Порядок

MVar операции всегда наблюдаются в порядке их записи в программе, независимо от модели памяти базовой машины. Это в отличие от IORef операций, которые в некоторых случаях могут показаться другому потоку вне порядка.

Пример

Рассмотрим следующую конкурирующую структуру данных — канал с пропуском. Это канал для периодического источника информации с высокой пропускной способностью (например, событий движения мыши.) Запись в канал никогда не блокирует, а чтение из канала возвращает только последнее значение или блокирует, если нет новых значений. Поддержка нескольких читателей с помощью операции dupSkipChan.

Канал с пропуском — это пара MVar. Первый MVar содержит текущее значение и список семафоров, которые необходимо уведомить при изменении. Второй MVar — это семафор для этого конкретного читателя: он заполнен, если в канале есть значение, которое этот читатель ещё не прочитал, и пуст в противном случае.

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)

Этот пример был адаптирован из оригинальной статьи Concurrent Haskell. Для получения дополнительных примеров использования MVar для создания примитивов синхронизации высокого уровня см. Chan и QSem.

MVars

data MVar a Source

MVar (произносится "эм-вар") — это синхронизирующая переменная, используемая для связи между конкурирующими потоками. Можно представить её как ящик, который может быть пустым или заполненным.

Примеры использования
Подробности примеров
Eq (MVar a) Source

Сравнивает указатели в основе.

С момента: base-4.1.0.0

Подробности примера

Определено в GHC.Internal.MVar

Методы

(==) :: MVar a -> MVar a -> Bool Source

(/=) :: MVar a -> MVar a -> Bool Source

newEmptyMVar :: IO (MVar a) Source

Создать MVar, который изначально пуст.

newMVar :: a -> IO (MVar a) Source

Создать MVar, который содержит предоставленное значение.

takeMVar :: MVar a -> IO a Source

Возвращает содержимое MVar. Если MVar в настоящее время пуст, takeMVar будет ожидать, пока он не заполнится. После takeMVar, MVar остаётся пустым.

Есть ещё две важные особенности takeMVar:

  • takeMVar — это однократное пробуждение. То есть, если несколько потоков заблокированы в takeMVar, и MVar заполняется, только один поток будет разбужен. Система выполнения гарантирует, что разбуженный поток завершит свою операцию takeMVar.
  • Когда несколько потоков заблокированы на MVar, они разбуживаются в порядке очереди «первый пришёл — первый обслужен». Это полезно для обеспечения справедливости абстракций, созданных с использованием MVar.

putMVar :: MVar a -> a -> IO () Source

Поместить значение в MVar. Если MVar в настоящее время заполнен, putMVar будет ожидать, пока он не станет пустым.

Есть ещё две важные особенности putMVar:

  • putMVar — это однократное пробуждение. То есть, если несколько потоков заблокированы в putMVar, и MVar становится пустым, только один поток будет разбужен. Система выполнения гарантирует, что разбуженный поток завершит свою операцию putMVar.
  • Когда несколько потоков заблокированы на MVar, они разбуживаются в порядке очереди «первый пришёл — первый обслужен». Это полезно для обеспечения справедливости абстракций, созданных с использованием MVar.

readMVar :: MVar a -> IO a Source

Атомарно прочитать содержимое MVar. Если MVar в настоящее время пуст, readMVar будет ожидать, пока он не заполнится. readMVar гарантированно получит следующее putMVar.

readMVar — это многократное пробуждение, поэтому когда несколько читателей заблокированы на MVar, все они разбуживаются одновременно. Система выполнения гарантирует, что все разбуженные потоки завершат свою операцию readMVar.

Примечание по совместимости: До версии base 4.7, readMVar был комбинацией takeMVar и putMVar. Это означало, что при наличии других потоков, пытающихся putMVar, readMVar мог заблокироваться. Кроме того, readMVar не получал бы следующий putMVar, если уже был заблокирован ожидающий поток на takeMVar. Старое поведение можно восстановить, реализовав `readMVar` следующим образом:

readMVar :: MVar a -> IO a
readMVar m =
  mask_ $ do
    a <- takeMVar m
    putMVar m a
    return a

swapMVar :: MVar a -> a -> IO a Источник

Извлечь значение из MVar, поместить новое значение в MVar и вернуть извлечённое значение. Эта функция атомарна только если нет других производителей для этого MVar. Другими словами, она не может гарантировать, что к моменту, когда swapMVar получит возможность записи в MVar, значение MVar не будет изменено операцией записи из другого потока.

tryTakeMVar :: MVar a -> IO (Maybe a) Источник

Неблокирующая версия takeMVar. Функция tryTakeMVar возвращается немедленно, с Nothing, если MVar была пустой, или с Just a, если MVar была полной с содержимым a. После tryTakeMVar, MVar остаётся пустой.

tryPutMVar :: MVar a -> a -> IO Bool Источник

Неблокирующая версия putMVar. Функция tryPutMVar пытается поместить значение a в MVar, возвращая True при успехе или False в противном случае.

isEmptyMVar :: MVar a -> IO Bool Источник

Проверить, пуст ли данный MVar.

Обратите внимание, что возвращаемое булево значение — это просто мгновенный снимок состояния MVar. К моменту, когда вы среагируете на его результат, MVar может быть заполнен (или опустошен) — будьте очень осторожны при использовании этой операции. Используйте tryTakeMVar вместо этого, если это возможно.

withMVar :: MVar a -> (a -> IO b) -> IO b Источник

withMVar — это защищённая от исключений обёртка для работы со содержимым MVar. Эта операция защищена от исключений: она заменит исходное содержимое MVar в случае возникновения исключения (см. Control.Exception). Однако она атомарна только если нет других производителей для этого MVar. Другими словами, она не может гарантировать, что к моменту, когда withMVar получит возможность записи в MVar, значение MVar не будет изменено операцией записи из другого потока.

withMVarMasked :: MVar a -> (a -> IO b) -> IO b Источник

Как withMVar, но действие IO во втором аргументе выполняется с замаскированными асинхронными исключениями.

С: base-4.7.0.0

modifyMVar_ :: MVar a -> (a -> IO a) -> IO () Источник

Защищённая от исключений обёртка для изменения содержимого MVar. Как и withMVar, modifyMVar заменит исходное содержимое MVar в случае возникновения исключения во время операции. Эта функция атомарна только если нет других производителей для этого MVar. Другими словами, она не может гарантировать, что к моменту, когда modifyMVar_ получит возможность записи в MVar, значение MVar не будет изменено операцией записи из другого потока.

modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b Источник

Небольшая вариация modifyMVar_ которая позволяет вернуть значение (b) в дополнение к изменённому значению MVar.

modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO () Источник

Как modifyMVar_, но действие IO во втором аргументе выполняется с замаскированными асинхронными исключениями.

С: base-4.6.0.0

modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b Источник

Как modifyMVar, но действие IO во втором аргументе выполняется с замаскированными асинхронными исключениями.

С: base-4.6.0.0

tryReadMVar :: MVar a -> IO (Maybe a) Источник

Неблокирующая версия readMVar. Функция tryReadMVar возвращается немедленно, с Nothing, если MVar была пустой, или с Just a, если MVar была полной с содержимым a.

С: base-4.7.0.0

mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a)) Источник

Создать указатель Weak на MVar, используя второй аргумент как финализатор для выполнения, когда MVar будет собран сборщиком мусора.

С: base-4.6.0.0

addMVarFinalizer :: MVar a -> IO () -> IO () Источник

Устарело: используйте mkWeakMVar вместо этого

© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/9.12.1/docs/libraries/base-4.21.0.0-8e62/Control-Concurrent-MVar.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API