C # multithreading (ii) thread synchronization basics (top)

Source: Internet
Author: User
Tags semaphore

The first article in this series briefly introduces the concept of threading and some simple operations on threading, starting with this article to explain thread synchronization, thread synchronization is the difficulty of multithreading technology. The thread Synchronization Foundation consists of the following parts:

1. Synchronization Essentials (Synchronization Essentials)

2. Lock (Locking)

3. Thread safety (threads Safety)

4. Event wait handle (signaling with event wait Handles)

5. Synchronization context (synchronization contexts)

Synchronization Essentials (Synchronization Essentials)

Thread synchronization can be divided into four categories:

1, the simple blocking method (Simple blocking methods, said an inappropriate analogy, the so-called synchronization is blocking the runners to stop and so on, and then March, which is called synchronization, how harmonious picture): There are three main methods Thread.Sleep, joins and task.wait.

2, Lock (Locking constructs): Only one thread is allowed to enter the critical section. Common locks are lock (monitor.enter/moitor.exit),mutex(mutex),SpinLock(Spin lock) , Semaphore(semaphore),Semaphoreslim , and read-write lock (reader/writer locks)

3. Signal (signaling constructs): This mechanism allows the thread to be paused until the outside notification is received. There are two main signaling mechanisms: The event wait handle (handles) and the wait and pluse methods of Monitor .. NET 4.0 Introduces countdownevent and Barrier class

4. Non-blocking thread synchronization (nonblocking synchronization constructs): The CLR and C # provide the following modes of non-blocking synchronization: thread.memorybarrier, thread.volatileread ,thread.volatilewrite, and volatile keywords, and interlocked classes.

The following one by one describe these four methods of synchronization:

A simple method of blocking

What is thread blocking : The execution of a thread is suspended for some reason, known as thread blocking.

A common way to block threads is to actively call the Thread.Sleep method for the execution thread to block itself and to block other threads from being blocked by the Join and EndInvoke methods to allow other threads to wait for the end of the thread execution. A blocked thread will give up the CPU resources to other threads.

When a thread is blocked or awakened (blocks or unblocks), the operating system completes the contextswitchprocess.

Wake-up occurs in the following 4 scenarios:

1, the blocking condition is satisfied (by theblocking condition being satisfied) The original sentence feels strange

2. Operation Time-out (if timeout is specified, timeout)

3, through the thread.interrupt interrupted

4, through the Thread.Abort gave up

When a thread pauses through the Suspend method (which is not recommended), it is not considered to be blocked.

Blocking VS Spin (Blocking Versus spining)

Sometimes we need a thread that is blocking until the specified condition is met. Signals (signaling) and locks (Locking) are well-adapted to our requirements. However, there is a simpler implementation: a thread can be implemented by spin (spining or translation to idle), such as:

 while (!proceed);  // ; Empty statement
Or
 while (DateTime.Now < Nextstarttime);

The empty statement above is a waste of CPU time, it is best to change the following way

 while (!proceed) Thread.Sleep (ten);

Thread state

We can get the state of the thread through the ThreadState property, and explicitly the transition relationship of the thread state

Second, lock (Locking)

Locks provide a mechanism for mutually exclusive access-allowing only one thread to enter a particular code fragment (a critical section). This section will start with lock and then talk about mutexes. The lock statement is relatively fast (consumes less resources than mutexes) and is simpler, but mutexes can be mutually exclusive across programs (such as allowing only the program to be opened), which is not possible with the lock statement.

. NET 4.0 introduces a SpinLock structure used in high concurrency scenarios .

Let's look at an example:

usingSystem;usingSystem.Threading;classapp{Static voidMain () {AppDomain.CurrentDomain.UnhandledException+=NewUnhandledexceptioneventhandler (currentdomain_unhandledexception);//Global Exception Capture         for(inti =0; I < -*10000; i++)        {            vart =NewThread (() =threadunsafe.go ());        T.start (); }    }    Static voidCurrentdomain_unhandledexception (Objectsender, UnhandledExceptionEventArgs e) {Exception Error=(Exception) e.exceptionobject; Console.WriteLine ("MyHandler caught:"+error.    Message); }}classthreadunsafe{Static int_val1 =1, _val2 =1;  Public Static voidGo () {if(_val2! =0) {Console.WriteLine (_val1/_val2);}//this has the risk of an exception with a divisor of 0_val2 =0; }}

Run results

Analysis under this program, consider that there are two threads call the Threadunsafe class of Go method, the first to assign _val2 to 0, the second is to do division with _VAL2, this will be packet exception, although the probability of this anomaly is very low, but in the case of a large number of repetitions is very easy to appear, I repeat this example 1 million times, which is the performance of thread insecurity.

Now solve the problem with the lock statement

classthreadunsafe{Static ReadOnly Object_locker =New Object();//use locks to troubleshoot thread insecurity    Static int_val1 =1, _val2 =1;  Public Static voidGo () {Lock(_locker) {if(_val2! =0) {Console.WriteLine (_val1/_val2);}//This does not have a packet divisor of 0 exceptions_val2 =0; }    }}

Comparison of some locks drawings

Monitor.Enter and Monitor.Exit

The lock statement in C # actually uses the try/finally and Monitor.Enter and Monitor.Exit methods, and we look at the IL generated by the lock code above, as shown in the section that I circled with a red frame.

So using monitor to solve our problem is also OK, the code is as follows:

classthreadunsafe{Static ReadOnly Object_locker =New Object(); Static int_val1 =1, _val2 =1;  Public Static voidGo () {monitor.enter (_locker); //using try/finally and Monitor mode        Try        {            if(_val2! =0) {Console.WriteLine (_val1/_val2);} _val2=0; }        finally{monitor.exit (_locker);} }}

In fact, the above code is flawed, consider a situation, a thread executed Monitor.Enter (_locker) after the hang (such as insufficient memory to throw a OutOfMemoryException exception), because it did not enter the try/finally So it can't release the lock because the thread is already dead, so it will never release the lock it has, unless the program restarts, which leads to a lock leak (leaked lock).

The solution to this problem is that as long as you put the monitor.enter into a try, it is really no flaw?

Try {    monitor.enter (_locker);     // consider that the front-end of the line is hung, or an out-of-memory exception, monitor has not had time to get to the lock object    if 0) {Console.WriteLine (_val1/_val2);}         0 ;} finally {Monitor.Exit (_locker);}

Because the program and enter the TRY statement block, then it must also enter the finally statement block, then the execution of Monitor.Exit (_locker) will occur when the synchronizationlockexception exception, The implication of this exception is that the current thread does not own the lock object.

. NET 4.0 In order to end this problem, the introduction of methods

Public static void Enter (object obj,ref bool LockTaken) gets an exclusive lock on the specified object and automatically sets a value that indicates whether the lock was acquired and LockTaken as TR The UE indicates that it has acquired to the lock, whereas the reverse represents not acquiring the lock

So the most correct wording is

BOOL false ; Try {    Monitor.Enter (_locker,ref  lockTaken);         if 0) {Console.WriteLine (_val1/_val2);}         0 ;} finally if (LockTaken) Monitor.Exit (_locker);}

In fact, the lock statement in. NET 4.0 is eventually translated into this at the IL level, and now look at the second red frame of the IL.

TryEnter

Moniter also provides the TryEnter method that can set the timeout to look under MSDN

Personally think the red box is the most useful, look at the example of MSDN, the code is self-evident

BOOLAcquiredlock =false; Try{monitor.tryenter (lockobject, -,refAcquiredlock); if(acquiredlock) {//Code that accesses resources is protected by the lock.        }      Else      {            //Code to deal with the fact that the lock is not acquired.        }  }  finally  {      if(Acquiredlock) {monitor.exit (lockobject); }  }

End of this article (the Code coloring Effect of Windows Live Writer is really poor-_-#)

C # multithreading (ii) thread synchronization fundamentals (UP)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.