Модуль Lazy.Mutexed
module Mutexed: sig .. end
Простые ленивые вычисления, защищённые мьютексом, к которым можно обращаться из нескольких доменов или потоков.
У этой реализации есть два недостатка:
- Она менее оптимизирована, чем
Lazy.t. - Для ожидания параллельной инициализации она использует стандартный библиотечный
Mutex, поэтому плохо взаимодействует с абстракциями волокон/задач пользовательского уровня (опасный пример см. в документацииLazy.Mutexed.force).
Типичный пример использования — код необязательной инициализации библиотеки, который требует умеренных вычислительных затрат или использует ресурсы. Автор библиотеки не хочет выполнять эту работу при запуске, поскольку она может оказаться ненужной, но использовать 'a Lazy.t некорректно, если библиотека может применяться в конкурентной среде. Можно использовать 'a Lazy.Mutexed.t, если блокирующее поведение приемлемо.
Примечание: 'a Lazy.t защищает от рекурсивного принудительного вычисления отложенного вычисления и вызывает Lazy.Undefined. С другой стороны, 'a Lazy.Mutexed.t попытается рекурсивно захватить мьютекс, что вызовет Sys_error.
См. примеры ниже.
type !'a t
val is_val : 'a t -> bool
is_val x возвращает true, если отложенное вычисление x уже было выполнено и его результатом является значение, а не исключение.
val from_val : 'a -> 'a t
from_val v — это отложенное вычисление, которое уже завершено и результатом которого является значение v.
val from_fun : (unit -> 'a) -> 'a t
from_fun f — это отложенное вычисление, которое при принудительном выполнении захватывает мьютекс и вызывает f.
val force : 'a t -> 'a
force x принудительно выполняет отложенное вычисление x. Если x уже было выполнено, Lazy.force x снова возвращает то же значение, не вычисляя его повторно. Если при выполнении было вызвано исключение, оно будет вызвано снова.
Если во время вычисления результата происходит параллельный вызов force, вызывающий поток будет заблокирован на Mutex.
-
Вызывает исключение
Sys_error, если отложенное вычисление принудительно выполняется в потоке, где оно уже выполняется. Это может произойти, если отложенное вычисление пытается рекурсивно выполнить само себя, но также возможно, если код отложенного вычисления содержит операцию `yield()` для абстракции волокон/задач пользовательского уровня и два волокна/две задачи пытаются параллельно выполнить отложенное вычисление в одном домене:(* This is wrong, a mutex-protected thunk should not yield control. *) let thunk = Lazy.Mutexed.from_fun (fun () -> ... Fiber.yield () ...) (* It may result in a same-thread [Sys_error] exception below. *) let wrong = Fiber.both (fun () -> Lazy.Mutexed.force thunk) (fun () -> Lazy.Mutexed.force thunk)Чтобы избежать таких ошибок, следует следить за тем, чтобы отложенные вычисления, защищённые мьютексом, не передавали управление за пределы своих подвычислений — не используя в них библиотеку потоков пользовательского уровня либо используя подходящие блокирующие функции или функции маскирования, если они доступны.
Примеры
Типичный пример использования — инициализация локального состояния библиотеки, которое используется её функциями.
let config = Lazy.Mutexed.from_fun (fun () ->
match Sys.getenv_opt "MYLIB_CONFIG_PATH" with
| None | Some "" -> Config.default ()
| Some path -> Config.read_from_path path
)
let entropy =
(* we use a mibibyte of random data from /dev/urandom *)
Lazy.Mutexed.from_fun (fun () ->
In_channel.with_open_bin "/dev/urandom" (fun chan ->
In_channel.really_input_string chan (1024 * 1024)
)
)
© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/api/Lazy.Mutexed.html