如何使用F#锁定对象?

时间:2011-11-22 13:14:14

标签: f# locking

假设我有以下代码:

let a = ref 4.

printfn "1) a = %g" !a

let t1 = System.Threading.Thread (fun () ->
  lock a (fun () ->
    printfn "locked"
    System.Threading.Thread.Sleep 1000
    printfn "unlocked") )
t1.Start()

System.Threading.Thread.Sleep 100

a := 8.
printfn "2) a = %g" !a

这给出了以下结果:

  

1)a = 4
  锁定
  2)a = 8

     

val a:float ref = {contents = 8.0;}
  val t1:System.Threading.Thread

     

解锁

当我锁定它时,为什么a等于8.?是否可以使用可变值和refs来锁定记录?

PS:我需要锁定一个对象,这个对象既可以由我访问,也可以由WCF同时访问。

3 个答案:

答案 0 :(得分:13)

我同意@Dmitry使用lock k (fun () -> ...)并不意味着你在k上阻止了变异;它确实意味着您获取了一个键k来访问一个对象。由于密钥是唯一的,因此您可以对对象进行互斥访问,以避免出现错误结果。

根据您的示例,在调试模式下运行以下代码会导致a任意结果为1,3或6。这些值可以通过一个线程访问旧值a而另一个线程尝试更新该单元格的情况来解释。

let a = ref 4;;

printfn "1) a = %i" !a

let t1 = System.Threading.Thread (fun () ->  
    printfn "locked in thread 1"    
    a:= !a + 2
    printfn "unlocked in thread 1"    
    )

let t2 = System.Threading.Thread (fun () ->  
    printfn "locked in thread 2"    
    a:= !a - 3
    printfn "unlocked in thread 2"    
    )

t1.Start()
t2.Start()

System.Threading.Thread.Sleep 1000 // wait long enough to get the correct value
printfn "2) a = %i" !a;;
System.Console.ReadKey() |> ignore;;

为了确保正确的结果(应该是3),您可以引入一个对象monitor,并且任何想要更新a的线程必须首先获得monitor:< / p>

let monitor = new Object()
let a = ref 4;;

printfn "1) a = %i" !a

let t1 = System.Threading.Thread (fun () ->  
    printfn "locked in thread 1"    
    lock monitor (fun () -> a:= !a + 2)
    printfn "unlocked in thread 1"    
    )

let t2 = System.Threading.Thread (fun () ->  
    printfn "locked in thread 2"    
    lock monitor (fun () -> a:= !a - 3)
    printfn "unlocked in thread 2"    
    )

t1.Start()
t2.Start()

System.Threading.Thread.Sleep 1000 // wait long enough to get the correct value
printfn "2) a = %i" !a;;
System.Console.ReadKey() |> ignore;;

答案 1 :(得分:10)

您似乎误解了锁定是如何工作的。

在C#中,“lock关键字确保一个线程不进入代码的关键部分而另一个线程在关键部分。如果另一个线程试图输入锁定的代码,它将等待,阻塞,直到该对象被释放。”因此它不能保护锁定的对象免受突变。而在F#lock中的工作原理完全相同。

顺便说一下,AFAIK,lock只是Monitor级的糖。

根据Don Syme的说法,锁定功能的定义实际上如下:

open System.Threading
let lock (lockobj:obj) f =
  Monitor.Enter lockobj
  try
    f()
  finally
    Monitor.Exit lockobj

UPDATE:因为锁定不会使对象成为只读,并且因为您无法控制WPF代码,所以您的问题的解决方案涉及向WPF访问的属性添加线程同步(并尽量不要阻止UI线程)或在UI线程上调度工作或者。在不知道确切问题的情况下很难分辨。嗯,好消息是网上有大量的信息。

UPDATE2:哎呀,我读过“WPF”而不是“WCF”。嗯,这会让你的生活更轻松。您只需要在WCF方法实现中添加线程同步,在大多数情况下,您可以不再担心阻塞它们。所以只需仔细添加锁到所有相关代码......

答案 2 :(得分:0)

情节扭曲 - 使用参考单元格,您不需要额外的 obj 来充当锁,即更改

<块引用>

a := 8.

<块引用>

lock a (fun _ -> a := 8.)

将确保两个线程都阻塞并等待单元格变为可用。