2010-06-11 80 views
5

下面的測試代碼(F#)沒有返回結果,我期望:.NET 4自旋鎖

let safeCount() = 
    let n = 1000000 
    let counter = ref 0 
    let spinlock = ref <| SpinLock(false) 
    let run i0 i1() = 
    for i=i0 to i1-1 do 
     let locked = ref false 
     try 
     (!spinlock).Enter locked 
     if !locked then 
      counter := !counter + 1 
     finally 
     if !locked then 
      (!spinlock).Exit() 
    let thread = System.Threading.Thread(run 0 (n/2)) 
    thread.Start() 
    run (n/2) n() 
    thread.Join() 
    !counter 

我期望的SpinLock相互排斥的計數器,因此,它返回計數爲1,000,000,但相反,它會返回較小的值,就好像沒有發生互斥一樣。

任何想法有什麼不對?

回答

3

編輯: Stephen Swensen有一種方法可以直接訪問下面的ref樣式SpinLock。 !返回一個結構的副本,所以不應該在這種情況下使用。

你可以在它的工作原理類(我嘗試使用一個靜態的和不可改變的自旋鎖無濟於事)

type SpinLockClass() = 
    let s = System.Threading.SpinLock(false) 
    member x.Enter locked = s.Enter(locked) 
    member x.Exit() = s.Exit() 

let safeCount() = 
    let n = 1000000 
    let counter = ref 0 
    let spinlock = SpinLockClass() 
    let run i0 i1() = 
    for i=i0 to i1-1 do 
     let locked = ref false 
     try 
     spinlock.Enter locked 
     if !locked then 
      counter := !counter + 1 
     finally 
     if !locked then 
      spinlock.Exit() 
    let thread = System.Threading.Thread(run 0 (n/2)) 
    thread.Start() 
    run (n/2) n() 
    thread.Join() 
    !counter 
+0

謝謝。看起來像類中的字段不會被複制,而是其他所有內容。如果這是正確的,那麼它也會影響其他應用程序,例如複雜算術,您希望避免複製結構(而C#目前比F#快得多)。 – 2010-06-11 23:32:01

8

SpinLock是一個值類型。當你取消引用你的spinLock變量(!spinLock)時,結構被複制,並且你進入/退出的鎖現在是不同的。

+0

這能解決嗎? – 2010-06-11 08:04:09

10

爲什麼自旋鎖結構被複制的原因是因爲包裝自旋鎖!是一個函數:結構體在作爲參數傳遞給函數或從函數返回時(或任何其他類型的賦值)被複制。但是,如果直接訪問ref單元格的內容,則不會進行復制。

let safeCount() = 
    let n = 1000000 
    let counter = ref 0 
    let spinlock = ref <| SpinLock(false) 
    let run i0 i1() = 
    for i=i0 to i1-1 do 
     let locked = ref false 
     try 
     spinlock.contents.Enter locked 
     if !locked then 
      counter := !counter + 1 
     finally 
     if !locked then 
      spinlock.contents.Exit() 
    let thread = System.Threading.Thread(run 0 (n/2)) 
    thread.Start() 
    run (n/2) n() 
    thread.Join() 
    !counter