我对并发编程很新,所以我遇到了一个我需要解决的死锁问题。
因此,对于下面的代码,它不会打印任何我怀疑必须存在死锁的内容,尽管我不太确定它是如何发生的。
let sleepMaybe() = if (random 4) = 1 then Thread.Sleep 5
type account(name:string) =
let balance = ref 1000
member this.Balance = lock this <| fun () -> !balance
member this.Name = name
member this.Withdraw amount = balance := !balance - (sleepMaybe(); amount)
member this.Deposit amount = balance := !balance + (sleepMaybe(); amount)
member this.Transfer (toAcc:account) amount =
lock this <| fun () -> lock toAcc <| fun () -> toAcc.Deposit amount
this.Withdraw amount
let doTransfers (acc:account) (toAcc:account) () =
for i in 1..100 do acc.Transfer toAcc 100
printfn "%s balance: %d Other balance: %d" acc.Name acc.Balance toAcc.Balance
let q2main() =
let acc1=account("Account1")
let acc2=account("Account2")
startThread (doTransfers acc1 acc2)
startThread (doTransfers acc2 acc1)
q2main()
答案 0 :(得分:3)
您正在锁定实例本身并要求锁定两个实例以传输内容。这是死锁的秘诀。
他们每个人都会等待对方无限期地放开他们的锁定。
如果有一次获取多个锁,总是以相同的顺序获取锁。也就是说,通过更改对象职责,尽量不要一次锁定多个锁。
例如,提款和存款是两个不相关的单独操作,但它们会修改余额。您正尝试使用锁保护余额。一旦帐户的余额发生变化,再也无法保持这种锁定。此外,我建议知道如何转移到其他帐户不是帐户的责任。
考虑到这一点,这里有一些消除死锁的变化。
type Account(name:string) =
let mutable balance = 1000
let accountSync = new Object()
member x.Withdraw amount = lock accountSync
(fun () -> balance <- balance - amount)
member x.Deposit amount = lock accountSync
(fun () -> balance <- balance + amount)
let transfer amount (fromAccount:Account) (toAccount:Account) =
fromAccount.Withdraw(amount)
toAccount.Deposit(amount)
答案 1 :(得分:2)
克里斯解释了死锁的原因,但解决方案必须涉及锁定两个帐户的整个转移(假设存款可能因透支而失败等)。你正在有效地争取一种形式的交易记忆。这是一种方法:
open System
open System.Threading
open System.Threading.Tasks
type Account(name) =
let mutable balance = 1000
member val Name = name
member __.Balance = balance
member private __.Deposit amount =
balance <- balance + amount
member val private Lock = obj()
member this.Transfer (toAccount: Account) amount =
let rec loop() =
let mutable retry = true
if Monitor.TryEnter(this.Lock) then
if Monitor.TryEnter(toAccount.Lock) then
this.Deposit(-amount)
toAccount.Deposit(amount)
Monitor.Exit(toAccount.Lock)
retry <- false
Monitor.Exit(this.Lock)
if retry then loop()
loop()
let printLock = obj()
let doTransfers (acc:Account) (toAcc:Account) threadName =
for i in 1..100 do
acc.Transfer toAcc 100
lock printLock (fun () ->
printfn "%s - %s: %d, %s: %d" threadName acc.Name acc.Balance toAcc.Name toAcc.Balance)
[<EntryPoint>]
let main _ =
let acc1 = Account("Account1")
let acc2 = Account("Account2")
Task.WaitAll [|
Task.Factory.StartNew(fun () -> doTransfers acc1 acc2 "Thread 1")
Task.Factory.StartNew(fun () -> doTransfers acc2 acc1 "Thread 2")
|]
printfn "\nDone."
Console.Read()
答案 2 :(得分:0)
这是dining philosophers problem。一般的解决方案是在获取锁之前对其进行排序。