在OCaml中线程安全懒惰

时间:2014-06-09 15:14:58

标签: ocaml mutex lazy-evaluation

我在思考线程安全的懒惰,即在强制时,我们需要确保进程是线程安全的。

这是我写的代码:

type 'a t = 
  | Delayed of (unit -> 'a)
  | Value of 'a
  | Exn of exn

type 'a lazy_t = Mutex.t * ('a t ref)

let lazy_of_value x = Mutex.create(), ref (Value x)

let lazy_of_fun f = Mutex.create(), ref (Delayed f)

let force (m, t) = 
  Mutex.lock m;
  let x =
    match !t with
      | Value v -> v 
      | Exn e -> Mutex.unlock m; raise e
      | Delayed f -> 
    try
      let v = f () in
      t := Value v;
      v
    with e -> t := Exn e; Mutex.unlock m; raise e
  in 
  Mutex.unlock m;
  x

基本上,我将一个懒惰类型的锁附加为一个新的懒惰类型。

我将整个力量过程包裹在锁定/解锁中,但我做的是正确的吗?

或者我只需要在处理Delayed

的情况时锁定/解锁

我有这个问题,因为我认为force只会在处理Delayed时写入/修改懒惰一次,但一旦确定就可能有很多读取。因此,如果我将整个过程包装起来,它肯定是线程安全的,但是对于常量锁定解锁会有糟糕的性能。我是对的吗?

1 个答案:

答案 0 :(得分:1)

正如评论中暗示的那样,你可以在延迟函数的评估中使用第二层懒惰:

type 'a t = 
  | Delayed of (unit -> 'a)
  | Value of 'a
  | Exn of exn

type 'a lazy_t = 'a t ref

let lazy_of_value x = ref (Value x)

let lazy_of_fun f =
  let m = Mutex.create () in 
  let v = lazy (f ()) in
  let rec r = ref (Delayed (fun () -> 
    Mutex.lock m;
    try
      let v = Lazy.force v in 
      r := Value v;
      Mutex.unlock m;
      v
    with e -> r := Exn e; Mutex.unlock m; raise e
  ))
  in r

let force t = 
  let x =
    match !t with
      | Value v -> v 
      | Exn e -> raise e
      | Delayed f -> f ()
  in 
  x

请注意,互斥锁已从外部ref值中完全消除。

来自你的评论:

  

如果有人想在f()进行阅读时会怎样?

两种可能性:

  1. 外部参考已经更新,新人只看到Value,所以没问题。

  2. 外部引用仍然是Delayed,这意味着新线程将尝试执行f。如果另一个帖子在Delayed已经被执行时想要lock,它将在lazy停止,并且当它继续时,内部Value将被强制执行。另一方面,如果在更新外部引用之后出现一个线程,它只会看到ref,因此不必通过互斥锁定机制 。我可以预见的唯一可能的问题是,如果使用Value v 1 对外ref进行第二次分配,实际上会导致在{{1}中设置不同的物理值因此,打破任何可能的进一步物理比较,但我不认为它可能会发生(我们需要一个大师来确认)。

  3. 编辑: 这是一个不使用Lazy.t的实现。

    type 'a t = 
      | Delayed of (unit -> 'a)
      | Value of 'a
      | Exn of exn
    
    type 'a lazy_t = 'a t ref
    
    type 'a inner = 'a t ref
    
    let lazy_of_value x = ref (Value x)
    
    let lazy_of_fun f =
      let m = Mutex.create () in 
      let v = ref (Delayed f) in
      let rec r = ref (Delayed (fun () -> 
        Mutex.lock m;
        try
          let v' =
            match !v with
              (* first evaluation *) 
              | Delayed f ->f ()
              (* other concurrent evaluations *)
              | Value v -> v 
              | Exn e -> raise e
          in
          v := Value v';
          r := Value v';
          Mutex.unlock m;
          v'
        with e -> r := Exn e; Mutex.unlock m; raise e
      ))
      in r
    
    let force t = 
      let x =
        match !t with
          | Value v -> v 
          | Exn e -> raise e
          | Delayed f -> f ()
      in 
      x
    

    警告:两个未经测试的代码。


    (1):如果至少有两个线程同时运行延迟函数,则会发生这种情况。