Spec-Zone.ru › Haskell 9

Data.IORef

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

Содержание

  • IORefs
    • Модель памяти

Описание

Изменяемые ссылки в монаде IO.

IORefs

data IORef a Источник

Изменяемая переменная в монаде IO.

>>> import GHC.Internal.Data.IORef
>>> r <- newIORef 0
>>> readIORef r
0
>>> writeIORef r 1
>>> readIORef r
1
>>> atomicWriteIORef r 2
>>> readIORef r
2
>>> modifyIORef' r (+ 1)
>>> readIORef r
3
>>> atomicModifyIORef' r (\a -> (a + 1, ()))
>>> readIORef r
4

См. также STRef и MVar.

Примеры использования
Подробности примеров
Eq (IORef a) Источник

Равенство указателей.

С версии: base-4.0.0.0

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

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

Методы

(==) :: IORef a -> IORef a -> Bool Источник

(/=) :: IORef a -> IORef a -> Bool Источник

newIORef :: a -> IO (IORef a) Источник

Создание новой IORef.

readIORef :: IORef a -> IO a Источник

Чтение значения из IORef.

Обратите внимание, что процессор, выполняющий поток, может переупорядочить чтения или записи в независимые области памяти. Подробнее см. Data.IORef.

writeIORef :: IORef a -> a -> IO () Источник

Запись нового значения в IORef.

Эта функция не создаёт барьер памяти и может быть переупорядочена с другими независимыми чтениями и записями в рамках потока, что может привести к проблемам при многопоточной обработке. В таких случаях рассмотрите использование atomicWriteIORef вместо этого. Подробнее см. Data.IORef.

modifyIORef :: IORef a -> (a -> a) -> IO () Источник

Изменение содержимого IORef, комбинируя readIORef и writeIORef. Это не атомарное обновление, рассмотрите использование atomicModifyIORef при работе в многопоточной среде.

Обратите внимание, что modifyIORef не применяет функцию строго. Это означает, что если программа вызывает modifyIORef много раз, но редко использует значение, в памяти будут накапливаться задержки, что приведёт к утечке памяти. Это распространённая ошибка при использовании IORef в качестве счётчика. Например, следующее, скорее всего, приведёт к переполнению стека:

ref <- newIORef 0
replicateM_ 1000000 $ modifyIORef ref (+1)
readIORef ref >>= print

Чтобы избежать этой проблемы, используйте modifyIORef' вместо этого.

modifyIORef' :: IORef a -> (a -> a) -> IO () Источник

Строгая версия modifyIORef. Это не атомарное обновление, рассмотрите использование atomicModifyIORef' при работе в многопоточной среде.

С версии: base-4.6.0.0

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

Атомарно изменяет содержимое IORef.

Эта функция полезна для безопасного использования IORef в многопоточной программе. Если у вас только один IORef, то использование atomicModifyIORef для доступа и изменения его предотвратит гонки.

Расширение атомарности до нескольких IORef проблематично, поэтому рекомендуется, если вам нужно сделать что-то более сложное, использование MVar вместо этого — хороший вариант.

Концептуально,

atomicModifyIORef ref f = do
  -- Begin atomic block
  old <- readIORef ref
  let r = f old
      new = fst r
  writeIORef ref new
  -- End atomic block
  case r of
    (_new, res) -> pure res

Действия в разделе, помеченном как «атомарный блок», не подвергаются вмешательству других потоков. В частности, значение в IORef не может измениться между вызовами readIORef и writeIORef.

Функция, предоставленная пользователем, применяется к значению, хранящемуся в IORef, давая новое значение для хранения в IORef и значение для возврата. После того, как новое значение (лениво) будет сохранено в IORef, atomicModifyIORef принудительно формирует пару результатов, но не принуждает ни один из компонентов результата. Для принудительного формирования обоих компонентов используйте atomicModifyIORef'.

Обратите внимание, что

atomicModifyIORef ref (_ -> undefined)

вызовет исключение в вызывающем потоке, но также установит значение в IORef, где оно может быть прочитано другими потоками.

Эта функция накладывает барьер памяти, предотвращая переупорядочение вокруг «атомарного блока»; см. Data.IORef для получения подробностей.

atomicModifyIORef' :: IORef a -> (a -> (a, b)) -> IO b Источник

Строгая версия atomicModifyIORef. Эта версия принуждает к формированию значения, хранящегося в IORef, и возвращаемого значения.

Концептуально,

atomicModifyIORef' ref f = do
  -- Begin atomic block
  old <- readIORef ref
  let r = f old
      new = fst r
  writeIORef ref new
  -- End atomic block
  case r of
    (!_new, !res) -> pure res

Действия в «атомарном блоке» не подвергаются влиянию других потоков. В частности, значение в IORef не может измениться между вызовами readIORef и writeIORef.

Новое значение устанавливается в IORef до принудительного формирования любого значения. Так

atomicModifyIORef' ref (x -> (x+1, undefined))

увеличит IORef и затем вызовет исключение в вызывающем потоке.

atomicModifyIORef' ref (x -> (undefined, x))

и

atomicModifyIORef' ref (_ -> undefined)

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

Эта функция накладывает барьер памяти, предотвращая переупорядочение вокруг «атомарного блока»; см. Data.IORef для получения подробностей.

С версии: base-4.6.0.0

atomicWriteIORef :: IORef a -> a -> IO () Источник

Вариант writeIORef. Префикс «атомарный» относится к тому факту, что он накладывает барьер переупорядочения, подобно atomicModifyIORef . Такая запись не будет переупорядочена с другими чтениями или записями даже на процессорах со слабой моделью памяти.

С версии: base-4.6.0.0

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

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

Модель памяти

Большинство современных архитектур процессоров (например, x86/64, ARM) имеют модель памяти, которая позволяет потокам переупорядочивать чтения с предыдущими записями в различные места, например, см. справочник по архитектуре x86/64, 8.2.3.4 Загрузки могут быть переупорядочены с предыдущими записями в различные места.

Из-за этого в конкурентной программе операции IORef могут появляться вне порядка для другого потока. В следующем примере:

import GHC.Internal.Data.IORef
import GHC.Internal.Control.Monad (unless)
import Control.Concurrent (forkIO, threadDelay)

maybePrint :: IORef Bool -> IORef Bool -> IO ()
maybePrint myRef yourRef = do
  writeIORef myRef True
  yourVal <- readIORef yourRef
  unless yourVal $ putStrLn "critical section"

main :: IO ()
main = do
  r1 <- newIORef False
  r2 <- newIORef False
  forkIO $ maybePrint r1 r2
  forkIO $ maybePrint r2 r1
  threadDelay 1000000

Возможна ситуация, когда строка "critical section" будет напечатана дважды, даже если нет переплетения операций двух потоков, позволяющего такой результат. Модель памяти x86/64 допускает, что readIORef произойдет до более раннего writeIORef.

Модель порядка памяти ARM обычно ещё слабее, чем x86/64, разрешая любое переупорядочение чтений и записей, пока они независимы с точки зрения текущего потока.

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

atomicWriteIORef, atomicModifyIORef и atomicModifyIORef' действуют как барьер для переупорядочения. Несколько вызовов этих функций происходят в строгом порядке программы, никогда не выполняясь раньше любых более ранних (в порядке программы) IORef операций или после любых последующих IORef операций.

© 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/Data-IORef.html

Spec-Zone.ru

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