In fact, if the threads are independent and do not involve any resource access, these non-interfering threads will not cause any problems. However, in practical applications, our threads always involve resource access, and often involve access to shared resources, which leads to thread synchronization issues. I always think that thread synchronization is a strange term. Literally, synchronization is to make the steps consistent. Does thread synchronization mean to make the threads access resources in a consistent way? In fact, thread synchronization happens to allow the thread to access resources in sequence rather than simultaneously, rather than synchronously accessing resources in the desired order ). In a word, multiple threads (not limited to the same process) may need to consider the means of thread synchronization if they need to access the same variable resources. There are also two common terms: thread security and thread conflict. The so-called thread conflict is caused by the problem of multi-thread access to shared resources. An operation is thread security, which indicates that this operation has no thread conflict problem, to achieve thread security, we must use thread synchronization methods. In the msdn class library, we can see that all methods indicate whether the method is thread-safe. If not, we use multiple threads.ProgramIf you use this method, you need to consider whether thread synchronization is required.
Since we want to keep the steps of a thread consistent, we can first think that if a thread is not completed, we will wait until it is completed:
StopwatchSw =Stopwatch. Startnew ();ThreadT1 =NewThread() => {Thread. Sleep (1000); Result = 100 ;}); t1.start ();Thread. Sleep (500 );While(T1.isalive );Console. Writeline (SW. elapsedmilliseconds );Console. Writeline (result );
Assuming that the thread writes the result to a static variable such as result after completion, it takes only 500 milliseconds for the main thread to do its own thing after it starts a new thread, next, we must wait until the thread computing is complete before proceeding to subsequent operations. At this time, we constantly ask if the thread still exists to see if the thread has completed the computation. After 500 milliseconds, we will return the result:
Because our main thread uses a loop to wait, it is not a wait for the processor. It fully calculates 500 milliseconds, that is, it wastes 500 milliseconds of processor resources. In fact, we can let the thread sleep for a short period of time in a loop, let out the processor time slice, and then round-robin and check again, although the results cannot be obtained after the thread completes, however, it saves processor resources:
StopwatchSw =Stopwatch. Startnew ();ThreadT1 =NewThread() => {Thread. Sleep (1000); Result = 100 ;}); t1.start ();Thread. Sleep (500 );While(T1.isalive)Thread. Sleep (500 );Console. Writeline (SW. elapsedmilliseconds );Console. Writeline (result );
The result is as follows:
In addition to polling, another method is to use join to block the main thread until the computing task of the next thread is completed:
StopwatchSw =Stopwatch. Startnew ();ThreadT1 =NewThread() => {Thread. Sleep (1000); Result = 100 ;}); t1.start ();Thread. Sleep (500); t1.join ();Console. Writeline (SW. elapsedmilliseconds );Console. Writeline (result );
The result is as follows:
Through the two examples, we review the usage of sleep and join again. Now let's take a look at the synchronization method using the lock mechanism:
StopwatchSw =Stopwatch. Startnew ();ThreadT1 =NewThread() => {Lock(Locker ){Thread. Sleep (1000); Result = 100 ;}}); t1.start ();Thread. Sleep (100 );Lock(Locker ){Console. Writeline (SW. elapsedmilliseconds );Console. Writeline (result );}
I think even ASP. NET applications often require lock to lock resources. IIS itself is a multi-threaded environment. If global cache is used in our applications, it is very likely that a website program will have multiple requests to modify the cache at the same time, at this time, we usually use lock to lock a reference type object. This object represents the lock range. At the same time, only one process can obtain the lock object, that is,CodeIt can only be executed by one thread at the same time. In the preceding example, after the thread is enabled, the main thread waits for a short period of time and the object is locked. This process lasts for one second, then the main thread can obtain the locker again after one second and output the result:
In this example, locker is defined as follows:
Static objectLocker =New Object();
The lock will be discussed in more detail. We have read the waiting and locking Synchronization Methods before. In any case, this is a passive method. Can it enable the thread to actively notify other waiting threads after completion? The answer is yes:
StopwatchSw =Stopwatch. Startnew ();ThreadT1 =NewThread() => {Thread. Sleep (1000); Result = 100; Are. Set () ;}); t1.start (); Are. waitone ();Console. Writeline (SW. elapsedmilliseconds );Console. Writeline (result );
Let's take a look at this piece of code. No matter what the objects are, we can understand that the main thread starts to wait after starting a new thread, then, after the thread completes the result in one second, it calls set (), which means that I have finished it and all the persons waiting for me can continue. The are object is defined as follows:
StaticEventwaithandleAre =NewAutoresetevent(False);
Autoresetevent is an automatic reset Event Notification method. Let me give you an example. You may understand it all at once. We can compare it to a subway gate, insert a ticket, and the gate allows a person. The value false in the constructor of autoresetevent indicates that the gate is disabled by default and can be passed only after a set is called, if you change it to true, you can see that the main thread has not waited, and the result is directly output. The so-called reset is the operation of shutting down the gate. That is to say, for autoresetevent, the gate is closed after a person uses a ticket, unless there are other people inserting a ticket (SET ). The running result of this program is as follows:
There is also a manualresetevent corresponding to autoresetevent.
StaticEventwaithandleMre =NewManualresetevent(False);
The only difference between the two is that the gate can still pass after a ticket is inserted. Unless someone manually sets a reset to close the gate, let's look at a simple example:
stopwatch Sw = stopwatch . startnew (); thread T1 = New thread () =>{ thread . sleep (1000); Result = 100; MRE. set () ;}); thread T2 = New thread () => {MRE. waitone (); console . writeline ( "get result" + Result + " and write to file ") ;}); t1.start (); t2.start (); MRE. waitone (); console . writeline (SW. elapsedmilliseconds); console . writeline (result);
For example, we open a thread for calculation. Not only does the main thread wait for the result to be displayed to the user, but another thread also waits for the result to be written into the log file, so that we can use the features of manualresetevent, because t2 or the main thread does not automatically shut down after it passes through the gate, both can pass. The output result is as follows:
As mentioned above, the autoresetevent is like a subway gate with one ticket, so let's look at a more vivid example:
Are. Set ();For(IntI = 0; I <10; I ++ ){NewThread() => {Are. waitone ();Console. Writeline (Datetime. Now. tostring ("Mm: SS"));Thread. Sleep (1000); Are. Set () ;}). Start ();}
We first call the Set Method of autoresetevent to make the gate accessible by default. Then, start 10 threads and wait in queue. If the thread enters, it takes one second to pass and close the gate. The output result is as follows:
One second, one person, as we expected. Manualresetevent will not be automatically closed after the gate has exceeded. This is equivalent to checking tickets by the teams of many tourist sites. The tour guide handed over 100 group tickets to the Ticket inspection personnel. After the number of Ticket inspection personnel reached a certain number, they were allowed in batches, then shut down the gate (autoresetevent will be used for scattered customers), and think about how we can write a program to simulate this batch entry into the gate:
First, you must use a variable to count the number of people entering the table. When the number of people reaches a certain number, the following variables are allowed:
Static intGroup = 0;
Then let's analyze this program:
For ( Int I = 0; I <10; I ++ ){ New Thread () => { Lock (Locker ){ Thread . Sleep (1000 ); // One person in one second Group ++;If (Group = 5) // Well, five people from the tour group will arrive {MRE. Set (); // The ticket reviewer said you should go in. Are. Set (); // The ticket reviewer opens the gate. Group = 0 ;}} MRE. waitone (); // Check the number of ticket visitors) Console . Writeline ( Datetime . Now. tostring ( "Mm: SS" )); // Enter. Group Entry Time Thread . Sleep (100 ); // Let Everyone finish Mre. Reset (); // After entering, the ticket reviewer stops releasing the ticket Are. waitone (); // Wait for the second wave to open the gate again }). Start ();}
In this example, manualresetevent and autoresetevent are used to simulate this group ticket check scenario. However, there is an interesting question here. Have you ever thought about this. before reset (), we sleep the thread for 100 milliseconds to restore all blocked threads and output the current time. If we comment out this sentence, we will find that there is a thread that calls MRE first when five threads are not completely restored. the reset () causes the gate to shut down, so this situation is hard to predict. This is equivalent to a thread synchronization problem during the synchronization thread. In fact,. Net has another built-in structure to implement this operation:
StaticSemaphoreS =NewSemaphore(2, 2 );
For(IntI = 0; I <10; I ++ ){NewThread() => {S. waitone ();Thread. Sleep (1000 );Console. Writeline (Datetime. Now. tostring ("Mm: SS"); S. Release () ;}). Start ();}
The result is as follows:
In this section, we read a lot of synchronization structures. Next we will continue to discuss some basic synchronization structures.