Spec-Zone.ru › Haskell 9

Control.Concurrent.MVar

Авторские права (c) The University of Glasgow 2001
Лицензия BSD-style (см. файл libraries/base/LICENSE)
Сопровождающий libraries@haskell.org
Стабильность стабильный
Переносимость непереносимый (параллелизм)
Safe Haskell Safe
Язык Haskell2010

Содержание

  • MVars

Описание

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

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

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

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

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

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

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

Ни один поток не может бесконечно долго блокироваться на MVar, если только другой поток не удерживает эту MVar бесконечно долго. Обычно гарантия справедливости реализуется так: потоки, заблокированные на MVar, обслуживаются в порядке FIFO (так работает GHC), однако семантика этого не гарантирует.

Подводные камни

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

Порядок выполнения

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

Пример

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

Канал с пропуском — это пара MVars. Первый 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. Другие примеры использования MVars для создания примитивов синхронизации более высокого уровня см. в 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, они пробуждаются в порядке FIFO. Это полезно для обеспечения справедливости абстракций, построенных с использованием MVars.

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

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

У putMVar есть ещё два важных свойства:

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

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 Source

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

tryTakeMVar :: MVar a -> IO (Maybe a) Source

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

tryPutMVar :: MVar a -> a -> IO Bool Source

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

isEmptyMVar :: MVar a -> IO Bool Source

Проверяет, пуста ли заданная MVar.

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

withMVar :: MVar a -> (a -> IO b) -> IO b Source

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

withMVarMasked :: MVar a -> (a -> IO b) -> IO b Source

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

Начиная с: base-4.7.0.0

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

Безопасная при возникновении исключений обёртка для изменения содержимого MVar. Как и withMVar, modifyMVar восстанавливает исходное содержимое MVar, если во время операции возникает исключение. Эта функция атомарна только при отсутствии других производителей для этой MVar. Иными словами, она не гарантирует, что к моменту, когда 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 во втором аргументе выполняется с маскировкой асинхронных исключений.

Начиная с: base-4.6.0.0

modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b Source

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

Начиная с: base-4.6.0.0

tryReadMVar :: MVar a -> IO (Maybe a) Source

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

Начиная с: base-4.7.0.0

mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a)) Source

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

Начиная с: base-4.6.0.0

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

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

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

Spec-Zone.ru

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