Assume I have the following code:
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
Which gives the following result:
1) a = 4
locked
2) a = 8val a : float ref = {contents = 8.0;}
val t1 : System.Threading.Threadunlocked
Why does a
equals 8.
when I locked it? Is it possible to lock a record with mutable values and refs?
PS: I need to lock an object which is both accessed by me and at the same time by WCF.
It seems that you misunderstand how locking works.
In C#, "The lock keyword ensures that one thread does not enter a critical section of code while another thread is in the critical section. If another thread tries to enter a locked code, it will wait, block, until the object is released." So it doesn't protect locked object from mutation. And in F# lock
works exactly in the same way.
By the way, AFAIK, lock
is just a sugar around Monitor class.
And according to Don Syme the definition of lock function actually looks like:
open System.Threading
let lock (lockobj:obj) f =
Monitor.Enter lockobj
try
f()
finally
Monitor.Exit lockobj
UPDATE: Because locking doesn't make an object read-only and because you have no control over WPF code, solutions to your problem involves adding thread synchronization to properties WPF accesses (and try not to block UI thread) or scheduling work on the UI thread or else. It's hard to tell without knowing the exact problem. Well, good thing is that there're tons of info on the Web.
UPDATE2: Oops, I've read "WPF" instead of "WCF". Well, that makes your life much easier. You just need to add thread synchronisation to your WCF methods implementation and in most cases you can stop worrying about blocking them. So just carefully add locks to all relevant code...
I agree with @Dmitry that using lock k (fun () -> ...)
doesn't mean that you prevent mutation on k
; it does mean that you acquire a key k
to access an object. Because the key is unique, you have mutual exclusive access to the object to avoid getting wrong results.
Based on your example, running this below code in Debug mode results in results of 1, 3 or 6 for a
arbitrarily. These values can be explained by the situation when one thread accessing the old value of a
and the other thread trying to update that cell.
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;;
To ensure the correct result (which should be 3), you can introduce an object monitor
, and any thread which would like to update a
has to acquire monitor
first:
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;;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With