как заблокировать объект с помощью F #? - PullRequest
14 голосов
/ 22 ноября 2011

Предположим, у меня есть следующий код:

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. когда я его запер?Можно ли заблокировать запись с изменяемыми значениями и ссылками?

PS: мне нужно заблокировать объект, к которому одновременно обращается и я, и WCF одновременно.

Ответы [ 2 ]

13 голосов
/ 22 ноября 2011

Я согласен с @Dmitry, что использование lock k (fun () -> ...) не означает, что вы предотвращаете мутацию на k;это означает, что вы получаете ключ k для доступа к объекту.Поскольку ключ уникален, у вас есть взаимный монопольный доступ к объекту, чтобы избежать получения неправильных результатов.

Исходя из вашего примера, выполнение приведенного ниже кода в режиме Отладка приводит к произвольным результатам 1, 3 или 6 для a.Эти значения могут быть объяснены ситуацией, когда один поток обращается к старому значению 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:

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;;
10 голосов
/ 22 ноября 2011

Кажется, вы неправильно понимаете, как работает блокировка.

В C # "ключевое слово lock гарантирует, что один поток не входит в критическую секцию кода, пока другой поток находится в критической секции. Если другой поток пытаетсячтобы ввести заблокированный код, он будет ждать, блокировать, пока объект не будет освобожден. "Таким образом, он не защищает заблокированный объект от мутации.А в F # lock работает точно так же.

Кстати, AFAIK, lock это просто сахар вокруг Monitor class.

И соответственноДону Сайму определение функции блокировки на самом деле выглядит следующим образом:

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

ОБНОВЛЕНИЕ: Поскольку блокировка не делает объект доступным только для чтения и поскольку у вас нет контроля над кодом WPF,Решение вашей проблемы включает добавление синхронизации потоков к свойствам доступа WPF (и попытка не блокировать поток пользовательского интерфейса) или планирование работы в потоке пользовательского интерфейса или в другом месте.Трудно сказать, не зная точную проблему.Что ж, хорошо, что в Интернете полно информации.

UPDATE2: Упс, я прочитал "WPF" вместо "WCF".Ну, это делает вашу жизнь намного проще.Вам просто нужно добавить синхронизацию потоков в реализацию методов WCF, и в большинстве случаев вы можете перестать беспокоиться о их блокировке.Так что просто аккуратно добавьте блокировки ко всему соответствующему коду ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...