When should & shouldn't I use this C# utility class to control threads via Interlocked

你离开我真会死。 提交于 2019-12-05 18:28:49

SpinLocks in general are a form of lock that keeps waiting threads awake (cycling over and over on a check condition in a tight loop- think "Mommy are we there yet?") rather than relying on heavier, slower, kernel mode signals. They are usually intended for situations where the expected wait time is very short, where they outperform the overhead of creating and waiting on an OS handle for a traditional lock. They incur more CPU cost than a traditional lock though, so for more than a very short wait time a traditional lock (like the Monitor class or the various WaitHandle implementations) is preferred.

This short wait time concept is demonstrated in your code above:

waitEvent.Reset();
// All that we are doing here is setting some variables.  
// It has to be atomic, but it's going to be *really* fast
uint& numPointer = numWaiters;
bool flag = false;
// And we are done.  No need for an OS wait handle for 2 lines of code.
this.myLock.Exit();

There is a perfectly good SpinLock built into the BCL, however it is only in v4.0+, so if you are working in an older version of the .NET framework or on code that was migrated from an older version, someone may have written their own implementation.

To answer your question: You should use the built-in SpinLock if you are writing new code on .NET 4.0 or higher. For code on 3.5 or older, especially if you are extending Nesper, I'd argue that this implementation is time-tested and appropriate. Only use a SpinLock where you know that the time a thread may wait on it is very small, as in the example above.

EDIT: Looks like your implementation came from Nesper- the .NET port of the Esper CEP library:

https://svn.codehaus.org/esper/esper/tagsnet/release_1.12.0_beta_1/trunk/NEsper/compat/SpinLock.cs

and

https://svn.codehaus.org/esper/esper/tagsnet/release_1.12.0_beta_1/trunk/NEsper/compat/FastReaderWriterLock.cs

I can confirm that Nesper existed long before the .NET framework 4, so that explains the need for a home-spun SpinLock.

It appears that the original author wanted a faster version of ReaderWriterLock. This old class was painfully slow. My own tests (which I did a long time ago) indicates that RWL had ~8x the overhead of a plain old lock. ReaderWriterLockSlim improved things quite a bit (though it still has ~2x the overhead as compared to lock). At this point I would say ditch the custom code and just use the newer ReaderWriterLockSlim class.

But, for what it is worth let me explain some of that custom SpinLock code.

  • Interlocked.CompareExchange is .NET's version of a CAS operation. It is the most fundamental synchronization primitive. You can literally build everything else from this single operation including your own custom Monitor-like class, reader writer locks, etc. Obviously it was used here to create a spin lock.
  • Thread.Sleep(0) yields to any thread of with the same or higher priority on any processor.
  • Thread.Sleep(1) yields to any thread on any processor.
  • Thread.SpinWait puts the thread into a tight loop for the specified number of iterations.

Although it was not used in the code you posted there is another useful mechanism for creating spin locks (or other low lock strategies).

  • Thread.Yield yields to any thread on the same processor.

Microsoft uses all of these calls in their highly concurrent synchronization mechanisms and collections. If you decompile SpinLock, SpinWait, ManualResetEventSlim, etc. you will see a fairly complex song-and-dance going on with these calls...much more complex than the code you posted.

Again, ditch the custom code and just use ReaderWriterLockSlim instead of that custom FastReaderWriterLock class.


By the way, this.lockHeld != null should produce a compiler warning since lockHeld is a value type.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!