Control.Concurrent.MVar
| Авторские права | (c) Университет Глазго 2001 |
|---|---|
| Лицензия | BSD-стиль (см. файл libraries/base/LICENSE) |
| Поддерживающий | libraries@haskell.org |
| Стабильность | экспериментальная |
| Переносимость | непереносимая (конкурентность) |
| Безопасный Haskell | Надёжный |
| Язык | Haskell2010 |
Содержание
Описание
Переменная MVar t — это изменяемая область памяти, которая либо пуста, либо содержит значение типа t. Она имеет две основные операции: putMVar, которая заполняет MVar, если она пуста, и блокируется в противном случае, и takeMVar, которая опустошает MVar, если она полна, и блокируется в противном случае. Они могут использоваться различными способами:
- В качестве синхронизированных изменяемых переменных,
- В качестве каналов, где
takeMVarиputMVarявляются операциями получения и отправки, и - В качестве двоичного семафора
MVar (), гдеtakeMVarиputMVarявляются операциями ожидания и сигнализации.
Они были введены в статье "Concurrent Haskell" Саймона Пейтона Джонса, Эндрю Гордона и Сигбьёрна Финне, хотя некоторые детали их реализации с тех пор изменились (в частности, запись в полную MVar ранее приводила к ошибке, но теперь просто блокирует.)
Применимость
MVarы предлагают большую гибкость, чем IORefы, но меньшую, чем STMы. Они подходят для построения синхронизирующих примитивов и выполнения простой межпотоковой коммуникации; однако они очень простые и уязвимы к гонкам, тупикам или неперехваченным исключениям. Не используйте их, если вам необходимо выполнять более сложные атомарные операции, такие как чтение из нескольких переменных: используйте STM вместо этого.
В частности, «большие» функции в этом модуле (readMVar, swapMVar, withMVar, modifyMVar_ и modifyMVar ) представляют собой просто композицию takeMVar и putMVar с безопасностью от исключений. Эти функции гарантируют атомарность только если все другие потоки выполняют takeMVar перед putMVar также; в противном случае они могут заблокироваться.
Справедливость
Ни один поток не может быть заблокирован неопределённо на MVar , если другой поток не удерживает это MVar неопределённо. Обычно гарантируется справедливость за счёт того, что потоки, заблокированные на MVar обслуживаются по принципу «первый пришёл — первый обслужен», но это не гарантируется в семантике.
Особенности
Как и многие другие структуры данных 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.
MVar
MVar (произносится как "эм-вар") — это синхронизируемая переменная, используемая для коммуникации между конкурирующими потоками. Можно представить её как ящик, который может быть пустым или заполненным.
newEmptyMVar :: IO (MVar a) Источник
Создаёт пустую MVar.
newMVar :: a -> IO (MVar a) Источник
Создаёт MVar содержащую заданное значение.
takeMVar :: MVar a -> IO a Источник
Возвращает содержимое MVar. Если MVar в данный момент пуста, takeMVar будет ждать, пока она не заполнится. После takeMVar, MVar становится пустой.
Существует две важные особенности takeMVar:
-
takeMVar— однопробуждающий. То есть, если несколько потоков заблокированы вtakeMVar, иMVarзаполняется, только один поток будет разбужен. Система гарантирует, что разбуженный поток завершит свою операциюtakeMVar. - Когда несколько потоков заблокированы на
MVar, они разбуждаются в порядке очереди «первый пришёл — первый обслужен». Это полезно для обеспечения свойств справедливости абстракций, построенных с помощьюMVar.
putMVar :: MVar a -> a -> IO () Источник
Помещает значение в MVar. Если MVar в данный момент полна, putMVar будет ждать, пока она не станет пустой.
Существует две важные особенности putMVar:
-
putMVar— однопробуждающий. То есть, если несколько потоков заблокированы вputMVar, иMVarстановится пустой, только один поток будет разбужен. Система гарантирует, что разбуженный поток завершит свою операциюputMVar. - Когда несколько потоков заблокированы на
MVar, они разбуждаются в порядке очереди «первый пришёл — первый обслужен». Это полезно для обеспечения свойств справедливости абстракций, построенных с помощьюMVar.
readMVar :: MVar a -> IO a Источник
Атомарно считывает содержимое MVar. Если MVar в данный момент пуста, readMVar будет ждать, пока она не заполнится. readMVar гарантированно получит следующее putMVar.
readMVar — многопробуждающий, поэтому, когда несколько читателей заблокированы на MVar, все они разбуждаются одновременно.
Примечание о совместимости: До версии 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.
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 Source
Проверить, пуст ли заданный MVar.
Обратите внимание, что возвращаемое значение типа boolean — это просто моментальный снимок состояния MVar. К моменту, когда вы отреагируете на результат, MVar может быть заполнен (или опустошен) — поэтому будьте очень внимательны при использовании этой операции. Используйте tryTakeMVar вместо этого, если возможно.
withMVar :: MVar a -> (a -> IO b) -> IO b Source
withMVar — это безопасная оболочка для работы с содержимым MVar. Эта операция безопасна для исключений: она заменит исходное содержимое MVar, если возникнет исключение (см. Control.Exception). Однако она атомарна только в том случае, если нет других производителей для этого MVar.
withMVarMasked :: MVar a -> (a -> IO b) -> IO b Source
Аналогично withMVar, но действие IO во втором аргументе выполняется с замаскированными асинхронными исключениями.
Since: 4.7.0.0
modifyMVar_ :: MVar a -> (a -> IO a) -> IO () Source
Безопасная оболочка для изменения содержимого MVar. Как и withMVar, modifyMVar заменит исходное содержимое MVar, если во время операции возникнет исключение. Эта функция атомарна только в том случае, если нет других производителей для этого MVar.
modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b Source
Небольшая вариация modifyMVar_, которая позволяет возвращать значение (b) помимо измененного значения MVar.
modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO () Source
Как modifyMVar_, но действие IO во втором аргументе выполняется с замаскированными асинхронными исключениями.
Since: 4.6.0.0
modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b Source
Аналогично modifyMVar, но действие IO во втором аргументе выполняется с замаскированными асинхронными исключениями.
Since: 4.6.0.0
tryReadMVar :: MVar a -> IO (Maybe a) Source
Неблокирующая версия readMVar. Функция tryReadMVar возвращается немедленно, с Nothing, если MVar было пустым, или Just a, если MVar было полным с содержимым a.
Since: 4.7.0.0
mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a)) Source
Создать указатель Weak на MVar, используя второй аргумент в качестве финализатора для выполнения при удалении MVar из мусора.
Since: 4.6.0.0
addMVarFinalizer :: MVar a -> IO () -> IO () Source
Deprecated: используйте mkWeakMVar вместо этого
© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/7.10.3/docs/html/libraries/base-4.8.2.0/Control-Concurrent-MVar.html