. NET synchronous and asynchronous atomic operations and spin locks (Interlocked, SpinLock) (9), cinterlockedlock
This additional link:. NET synchronization and Asynchronization lock (ReaderWriterLockSlim) (8)
As mentioned above, although locking can effectively solve the competition conditions, it also has a negative impact: performance. Is there a better solution? Yes. Atomic operations, that is, Interlocked.
1. Let's first look at a counting atomic operation Demo /// <summary> /// atomic operation-counting /// </summary> public void Demo1 () {Task. run () => {long total = 0; long result = 0; PrintInfo ("counting"); Parallel. for (0, 10, (I) =>{ for (int j = 0; j <10000000; j ++) {Interlocked. increment (ref total); result ++ ;}}); PrintInfo ($ "the operation result should be \ t: {10*10000000 }"); printInfo ($ "atomic operation result \ t: {total}"); PrintInfo ($ "I ++ Operation Result \ t: {result }");});}Atomic operation-count
According to the Demo above, Interlocked can ensure that the counting operation of 64-bit integer values can meet expectations, while the ordinary I ++ operation has a competitive condition.
Interlocked provides many methods for integer operations, which are not described here.
2. Different Singleton Modes
Interlocked provides the generic version of The Interlocked. CompareExchange <T> method. Let's take a look at a clever usage of this generic version.
/// <Summary> // atomic operation-singleton mode // </summary> public void Demo2 () {ConcurrentQueue <InterlockedSingleClass> queue = new ConcurrentQueue <Demo. interlockedSpinLockClass. interlockedSingleClass> (); // although this test is not rigorous, it also illustrates some problems more or less. for (int I = 0; I <10; I ++) // too many threads are allocated at the same time, but the Scheduler cannot schedule {Task. run () => {var result = InterlockedSingleClass. singleInstance; queue. enqueue (result);} // The result Task is displayed after 1 second. delay( 1000 ). ContinueWith (t) => {PrintInfo ($ "using atomic operations-singleton mode, total number of generated objects: {queue. count} "); InterlockedSingleClass firstItem = null; queue. tryDequeue (out firstItem); for (int I = 0; I <queue. count;) {InterlockedSingleClass temp = null; queue. tryDequeue (out temp); if (temp = null | firstItem = null |! Object. referenceEquals (temp, firstItem) {PrintInfo ("Singleton mode failed") ;}} PrintInfo ("atomic operation-singleton mode-running completed ");});} public class InterlockedSingleClass {private static InterlockedSingleClass single = null; public static InterlockedSingleClass SingleInstance {get {// if (single = null) // comment out {Interlocked. compareExchange <InterlockedSingleClass> (ref single, new InterlockedSingleClass (), null) ;}return single ;}}}Atomic operation-singleton Mode
For the Interlocked. CompareExchange <T> method, I will introduce the following two sentences:
1. The first parameter is the ref parameter. If the first parameter and the third parameterReferences are equalTo replace the value of the first parameter with the second parameter, and return the original value of the first parameter.
2. This generic method only accepts parameters of the class type.
Iii. spin lock
Spin lock: provides a mutually exclusive lock primitive. In this element, the thread trying to obtain the lock will wait in the loop of repeated checks until the lock becomes available.
/// <Summary> // spin lock Demo, source: MSDN // </summary> public void Demo3 () {SpinLock sl = new SpinLock (); stringBuilder sb = new StringBuilder (); // Action taken by each parallel job. // Append to the StringBuilder 10000 times, protecting // access to sb with a SpinLock. action action = () => {bool gotLock = false; for (int I = 0; I <10000; I ++) {gotLock = false; try {sl. enter (ref gotLock); sb. append (I % 10 ). toString ();} finally {// Only give up the lock if you actually acquired it if (gotLock) sl. exit () ;}}; // Invoke 3 concurrent instances of the action above Parallel. invoke (action, action, action); // Check/Show the results PrintInfo ($ "sb. length = {sb. length} (shocould be 30000) "); PrintInfo ($" number of occurrences of '5' in sb: {sb. toString (). where (c => (c = '5 ')). count ()} (shocould be 3000 )");}Spin lock
After reading the Demo, let's take a closer look at the spin lock:
1. The spin lock itself is a structure rather than a class. using too many locks will not cause GC pressure.
2. The spin lock attempts to obtain the lock in a loop wait manner. That is to say, it will always occupy the CPU during the waiting period. If the waiting time is too long, it will cause a CPU waste, monitor will Sleep ).
3. Use of spin locks: make the critical section as short as possible (for a short time) and non-blocking. (Because a long wait time will cause a waste of CPU resources)
4. Because the spin lock is a loop wait mode, the execution mode is different from the Monitor sleep mode, the execution speed of the spin lock will be faster. The sleep mode of Monitor causes additional system overhead, and the execution speed decreases.
For the moment, the next article will introduce the WaitHandler family in the order of the previous directories. I want to change the next order and next time: Closure in concurrency.
Appendix, Demo: http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
See more: Casual guide: synchronous and asynchronous
(To be continued ...)