我正在使用该库编写一个相当异步的程序Thread。我将有很多不同的线程需要访问string list ref,如果不使用 a ,这安全吗Mutex?我今天早些时候了解到这Hashtbl需要 a Mutex,但我找不到有关 的任何信息ref。
简而言之,如果您对可变共享资源进行并发写入,则应该使用互斥体(或原子)来保护它们。
更详细地说,有三点需要牢记。
首先,OCamlthreads不是并行的。在 OCaml 4 及之前版本中,OCaml 运行时使用运行时锁来确保在任何时间点只有一个 OCaml 线程执行。在 OCaml 5 中,并行性的单位是域,为了保持向后兼容性,每个域都使用域锁来确保域中只有一个 OCaml 线程执行。
其次,OCaml 始终是内存安全的。即使存在竞争条件,内存访问也能保证类型正确。作为参考,这意味着您从参考中读取的所有值都将是写入参考中的值;而不是你的程序状态的一些奇怪的合并。
但是,如果没有同步,就不能保证并发程序的行为与等效的顺序程序相同。
例如,以下程序将到达assert false子句
let global = ref []
let sum = ref 0
let incr () =
let state = !global in
let a = Array.init 1_000 Fun.id in
let updated = a :: state in
global := updated
let decr () =
match !global with
| [] -> assert false
| _ :: q ->
global := q
let balanced () =
let incrs = List.init 100 (fun _ -> Thread.create incr ()) in
let () = List.iter Thread.join incrs in
let decrs = List.init 100 (fun _ -> Thread.create decr ()) in
List.iter Thread.join decrs
let () =
while true do balanced () done
Run Code Online (Sandbox Code Playgroud)
即使所有对incr和 的调用decr都非常平衡。原因是read和中write的共享引用不能保证同时发生。因此,两个调用可能会以这种方式交错:globalincrdecrincr
(* first call to incr *) | (* Second call to incr *)
let state = !global in |
let a = Array.init 1_000 Fun.id in |
| let state = !global in
let updated = a :: state in |
global := updated | let a = Array.init 1_000 Fun.id in
| let updated = a :: state in
| global := updated
Run Code Online (Sandbox Code Playgroud)
这意味着第二次调用将incr删除第一个调用所做的更新,并且在两次调用后,incr我们最终在全局列表中只有一个新元素。
因此,只要您可能对同一可变共享资源进行并发写入,同步原语就必不可少。
第三,在 OCaml 5(也称为并行性)中,引用不能用作同步原语。这就是引用和原子之间的区别。特别是,如果您有
module M: sig
val update: unit -> unit
val read: unit -> int option
end = struct
let written = ref false
let answer = ref 0
let update () =
answer := 1;
written := true
let read () =
if !written then Some !answer else None
end
Run Code Online (Sandbox Code Playgroud)
那么在某些CPU架构上,它可能会发生而不是read ()返回Some 0,因为不能保证answer在写入之前看到写入written。
| 归档时间: |
|
| 查看次数: |
97 次 |
| 最近记录: |