开发者

.NET 4 SpinLock

The following test code (F#) is not returning the result I'd expect:

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))
  threa开发者_StackOverflow中文版d.Start()
  run (n/2) n ()
  thread.Join()
  !counter

I'd expect the SpinLock to mutually exclude the counter and, therefore, for it to return counts of 1,000,000 but, instead, it returns smaller values as if no mutual exclusion is occurring.

Any ideas what's wrong?


The reason why the SpinLock struct is being copied is because ! is a function: structs are copied when passed as arguments to a function or returned from a function (or any other kind of assignment for that matter). However, if you access the contents of the ref cell directly, no copying takes place.

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


SpinLock is a value type. When you dereference your spinLock variable (!spinLock), the struct got copied, and the lock you enter/exit is now different.


EDIT: Stephen Swensen has a way to directly access a ref style SpinLock below. ! returns a copy of structs so should not be used in this case.

You can wrap SpinLock in a class it works (I tried using a static and immutable SpinLock to no avail)

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜