Part V: 同期と排他制御¶
概要¶
本章では、銀行口座を例に同期とデッドロック回避を学びます。
銀行口座レコード¶
type BankAccount = {
Lock: obj
mutable Balance: int
}
module BankAccount =
/// Create a new bank account
let createAccount (initialBalance: int) : BankAccount =
{ Lock = obj(); Balance = initialBalance }
/// Get the current balance
let getBalance (account: BankAccount) : int =
lock account.Lock (fun () -> account.Balance)
/// Deposit money into the account
let deposit (account: BankAccount) (amount: int) : unit =
lock account.Lock (fun () ->
account.Balance <- account.Balance + amount
)
/// Withdraw money from the account
let withdraw (account: BankAccount) (amount: int) : bool =
lock account.Lock (fun () ->
if account.Balance >= amount then
account.Balance <- account.Balance - amount
true
else
false
)
デッドロック回避¶
open System.Runtime.CompilerServices
/// Transfer money between accounts atomically, avoiding deadlock
let transfer (from: BankAccount) (toAccount: BankAccount) (amount: int) : bool =
// Always lock in consistent order to avoid deadlock
let fromHash = RuntimeHelpers.GetHashCode(from)
let toHash = RuntimeHelpers.GetHashCode(toAccount)
let (first, second) =
if fromHash < toHash then (from, toAccount)
else (toAccount, from)
lock first.Lock (fun () ->
lock second.Lock (fun () ->
if from.Balance >= amount then
from.Balance <- from.Balance - amount
toAccount.Balance <- toAccount.Balance + amount
true
else
false
)
)
F# の lock 関数¶
// F# の lock 関数は以下のパターンを簡潔に書ける
lock lockObj (fun () ->
// クリティカルセクション
doSomething()
)
// これは以下と等価
Monitor.Enter(lockObj)
try
doSomething()
finally
Monitor.Exit(lockObj)
次のステップ¶
Part VI では、ノンブロッキング I/O を学びます。