Thread is also crazy ---- thread synchronization (1), ---- Thread Synchronization
Preface
When threads in the thread pool are blocked, the thread pool creates additional threads, and the memory resources required to create, destroy, and schedule threads are quite expensive. In addition, many developers are used to creating more threads when they see that their program threads are not doing anything useful. In order to build scalable and responsive programs, we have introduced thread crazy-asynchronous programming.
However, asynchronous programming also has serious problems. If two different threads access the same variables and data, follow the implementation method of asynchronous functions, it is impossible for two threads to access the same data at the same time. In this case, we need thread synchronization. When multiple threads access Shared data at the same time, thread synchronization can prevent data corruption. This concept is emphasized because thread synchronization is essentially a timing problem.
Asynchronous mode is opposite to synchronous mode. synchronous mode is sequential mode. After one execution is complete and then the next execution is performed, it is necessary to wait and coordinate the operation. Asynchronization is independent of each other. You do not need to wait for the event to complete before proceeding. Threads are asynchronous. Asynchronous means that the main thread that calls the method does not need to wait for the completion of another thread synchronously, so that the main thread can do other things.
Basic concepts of primitive user mode and Kernel Mode Construction
Primitive: simple construction that can be used in code
User Mode: through special CPU commands to coordinate threads, the operating system will never detect a thread blocking the construction of the primitive user mode.
Kernel Mode: Provided by windows, the function implemented by the kernel is called in the application thread.
User Mode Constructor
C # the compiler, JIT compiler, and CPU all optimize the code. They try to keep our intent as much as possible. But from the multi-thread perspective, our intent is not always retained, the following is an example:
1 static void Main (string [] args) 2 {3 Console. writeLine ("stop the worker function after 5s"); 4 var t = new Thread (Worker); 5 t. start (); 6 Thread. sleep (5000); 7 stop = true; 8 9 Console. readLine (); 10} 11 12 private static bool stop = false; 13 14 private static void Worker (object obj) 15 {16 int x = 0; 17 while (! Stop) 18 {19 x ++; 20} 21 Console. WriteLine ("worker function stop x = {0}", x); 22}
If the compiler checks that stop is false, it generates code to enter an infinite loop and increments x continuously in the loop. Therefore, the optimization loop is completed quickly, but the compiler only checks stop once, not every time.
Example 2 --- two threads simultaneously access:
1 class test 2 {3 private static int m_flag = 0; 4 5 private static int m_value = 0; 6 7 public static void Thread1 (object obj) 8 {9 m_value = 5; 10 m_flag = 1; 11 12} 13 14 public static void Thread2 (object obj) 15 {16 if (m_flag = 1) 17 Console. writeLine ("m_value = {0}", m_value); 18} 19 20 // thread synchronization problem occurs only on multi-core CPU machines 21 public void Exec () 22 {23 var thread1 = new Thread (Thread1); 24 var thread2 = new Thread (Thread2); 25 thread1.Start (); 26 thread2.Start (); 27 Console. readLine (); 28} 29}
During program execution, the compiler must read the m_flag and m_value variables from RAM into the CPU register. RAM first passes the m_value 0, and thread1 changes the value to 5, but thread2 does not know that thread2 still thinks the value is 0. This problem generally occurs in a higher probability of multi-core CPU. The more CPUs, the higher the chance of multiple threads accessing resources at the same time.
The keyword volatile is used to prohibit Optimization of C # compiler, JTP compiler, and CPU execution. If it is used for variables, fields cannot be cached in the CPU register, make sure that all fields are read and written in RAM.
Mutual lock Construction
System. threading. each method in the Interlocked class performs an atomic read and write operation. Any variable written before calling an Interlocked method is executed before the Interlocked method is called, any variable after the call is read after the call.
The Interlocked method is used to perform static Add, Decrement, Compare, Exchange, and CompareChange operations on INT32 variables. It also accepts parameters of the object, Double, and other types.
Atomic operation: it refers to an operation that will not be interrupted by the thread scheduling mechanism. Once this operation starts, it will continue to run until the end, and there will be no context switch (switch to another thread) in the middle ).
Code Demonstration:
Note: The Interlocked method is used to asynchronously query several web servers and return data at the same time. The result is executed only once.
// Report status type enum CoordinationStatus {Cancel, Timeout, AllDone}
1 class AsyncCoordinator 2 {3 // AllBegun internally calls JustEnded to decrease it 4 private int _ mOpCount = 1; 5 6 // 0 = false, 1 = true 7 private int _ mStatusReported = 0; 8 9 private Action <CoordinationStatus> _ mCallback; 10 11 private Timer _ mTimer; 12 13 // call 14 public void AboutToBegin (int opsToAdd = 1) 15 {16 Interlocked. add (ref _ mOpCount, opsToAdd); 17} 18 19 // call 20 public void JustEnded () 21 {22 if (Int Erlocked. decrement (ref _ mOpCount) = 0) 23 {24 ReportStatus (CoordinationStatus. allDone); 25} 26} 27 28 // This method must call 29 public void AllBegin (Action <CoordinationStatus> callback, int timeout = Timeout. infinite) 30 {31 _ mCallback = callback; 32 if (timeout! = Timeout. infinite) 33 {34 _ mTimer = new Timer (TimeExpired, null, timeout, Timeout. infinite); 35 JustEnded (); 36} 37} 38 39 private void TimeExpired (object o) 40 {41 ReportStatus (CoordinationStatus. timeout); 42} 43 44 public void Cancel () 45 {46 ReportStatus (CoordinationStatus. cancel); 47} 48 49 private void ReportStatus (CoordinationStatus) 50 {51 // If the status has never been reported, report it; otherwise, ignore it, call 52 if (Interlocked. exchange (ref _ mStatusReported, 1) = 0) 53 {54 _ mCallback (status); 55} 56} 57}
1 class MultiWebRequest 2 {3 // The helper class is used to coordinate all asynchronous operations. 4 private AsyncCoordinator _ mac = new AsyncCoordinator (); 5 6 protected Dictionary <string, object> _ mServers = new Dictionary <string, object> 7 {8 {"http://www.baidu.com", null}, {"http://www.Microsoft.com", null}, {"http://www.cctv.com", null }, 9 {"http://www.souhu.com", null}, {"http://www.sina.com", null}, {"http://www.tencent.com", null}, 10 {"http://www.you" Ku.com ", null} 11}; 12 13 private Stopwatch sp; 14 public MultiWebRequest (int timeout = Timeout. infinite) 15 {16 sp = new Stopwatch (); 17 sp. start (); 18 // One-time request 19 var httpclient = new HttpClient (); 20 21 foreach (var server in _ mServers. keys) 22 {23 _ mac. aboutToBegin (1); 24 25 httpclient. getByteArrayAsync (server ). continueWith (task => ComputeResult (server, task); 26} 27 _ mac. allBegin (AllDone, timeout ); 28 Console. writeLine (""); 29} 30 31 private void ComputeResult (string server, Task <Byte []> task) 32 {33 object result; 34 if (task. exception! = Null) 35 {36 result = task. exception. innerException; 37} 38 else39 {40 // handle IO41 result = task in the thread pool. result. length; 42} 43 44 // Save the Length of the returned result 45 _ mServers [server] = result; 46 47 _ mac. justEnded (); 48} 49 50 public void Cancel () 51 {52 _ mac. cancel (); 53} 54 55 private void AllDone (CoordinationStatus) 56 {57 sp. stop (); 58 Console. writeLine ("total response time {0}", sp. elapsed); 59 switch (status) 60 {61 case CoordinationStatus. cancel: 62 Console. writeLine ("Operation canceled"); 63 break; 64 case CoordinationStatus. allDone: 65 Console. writeLine ("the result of the operation is as follows"); 66 foreach (var server in _ mServers) 67 {68 Console. writeLine ("{0}", server. key); 69 object result = server. value; 70 if (result is Exception) 71 {72 Console. writeLine ("error cause {0}", result. getType (). name); 73} 74 else75 {76 Console. writeLine ("returned bytes: {0}", result); 77} 78} 79 break; 80 case CoordinationStatus. timeout: 81 Console. writeLine ("Operation timeout"); 82 break; 83 default: 84 throw new ArgumentOutOfRangeException ("status", status, null); 85} 86} 87}
We strongly recommend that you refer to the above Code. This model is often used when I access the server.
Simple spin lock
1 class SomeResource 2 {3 private SimpleSpinLock s1 = new SimpleSpinLock (); 4 5 public void AccessResource () 6 {7 s1.Enter (); 8 // a thread can access 9 s1.Leave (); 10 11} 12} 13 14 class SimpleSpinLock15 {16 private int _ mResourceInUse; 17 18 public void Enter () 19 {20 while (true) 21 {22 if (Interlocked. exchange (ref _ mResourceInUse, 1) = 0) 23 return; 24} 25} 26 27 public void Leave () 28 {29 Volatile. write (ref _ mResourceInUse, 1); 30} 31}
This is a simple implementation of a thread synchronization lock. The biggest problem with this lock is that, in the case of competition, it will cause the thread to "Spin", which will waste valuable CPU time, organize the CPU to do more work, so this spin lock should be used to protect the very fast code that is executed.
In the next article, we will continue to explain thread synchronization, kernel mode construction, and hybrid thread synchronization construction. We hope this content will help you grow together. If you find any blog errors, please give your valuable comments in a timely manner, to avoid misleading!