One. Multi-threaded concept
Windows
Is a multitasking system, if you are using
WINDOWS 2000
And the above version, you can view programs and processes running the current system through the Task Manager. What is a process? When a program starts running, it is a process, the process refers to the memory and system resources used by the program and the program in the run. And a process is composed of multiple threads, threads are one execution stream in the program, and each thread has its own proprietary registers.
(
Stack pointer, program counter, etc.
)
However, the code area is shared, that is, different threads can perform the same function. Multithreading means that multiple execution streams are included in the program, that is, the individual tasks can be done in one program to perform different tasks in a program, which means that a single program is allowed to create multiple parallel execution threads. The browser is a very good multi-threaded example.
,
You can download in your browser
Java
Simultaneous scrolling page while small applications or images
,
When visiting the new page
,
Play animation and sound
,
Print file, etc.
The advantage of multi-thread is that it can improve
CPU
Utilization
-
Any programmer does not want his own procedure to be dry, in multithreaded programs, a thread must wait,
CPU
You can run other threads rather than waiting, this greatly improves the efficiency of the program.
However, we must also realize that threads themselves may affect system performance, using threads correctly:
The thread is also a program, so the thread needs to take up memory, the more the thread occupy the memory, the more threads need to coordinate and manage, so the access to the shared resource between the CPU time tracking thread must solve the problem of bidding resources. There is too much thread to cause control too complicated, which can eventually cause a lot of bugs.
Based on the above understanding, we can have a metaphor to deepen understanding. Suppose there is a company, there are many staff members in the company, then we can think that this normal operation is a process, and the staff in the company is thread. A company has to have a staff member, which is the same, and a process contains at least one thread. In the company, you can do everything in a staff, but efficiency is obviously high, a person's company is impossible to do it; a program can only use a thread to do things, in fact, some time The language is like Fortune, Basic is the same, but like a person's company, the efficiency is low, if you do a big program, the efficiency is lower - in fact there is now no single-threaded commercial software. The more the company's staff, the more the boss gives them, but also pays a lot of energy to manage them, coordinate the contradictions and interests between them; the procedure is the same, the more resources, the more resources, the needs The CPU time goes to track the thread, but also solves problems such as dead locks, synchronization. In short, if you don't want your company being called "leather bag company", you have several employees; if you don't want your program to be young, introduce multi-thread in your program! This article will explore the multi-thread mechanism in C # programming, solve problems such as thread control, multi-line inter-block communications through some instances. In order to save the cumbersome steps of the GUI, more clearly approximate the nature of the thread, all the procedures below are console programs, the last console.readline () is to stop the program to see the process of implementation Output. Ok, talk less, let us experience the multi-threaded C #! II. Manipulating a thread Any program is executed, at least one main thread, the following small program can give the reader an intuitive impression:
//SystemThread.csusing System; using System.Threading; namespace ThreadTest {class RunIt {[STAThread] static void Main (string [] args) {Thread.CurrentThread.Name = "System Thread"; // current thread to a named "System Thread" Console.WriteLine (thread.currentthread.name "'status:" thread.currentthread.threadstate; console.readline ();}}} What did you see after compiling? Yes, the program will produce the following: System Thread's Status: Running Here, we get the currently executed thread through the static property of the Thread class, assigning "System Thread", and finally outputs its current state in its Name property ThreadState. The so-called static properties are all the properties of this class all objects, whether you have created how many such classes of this class, but the static properties of the class have only one in memory. It is easy to understand why CurrentThread is static - although there are multiple threads exist at the same time, but at a moment, the CPU can only perform one. As demonstrated by the above program, we create and control threads through the Thread class. Note the head of the program, we used the following namespace:
Using system; using system.threading;
In .NET Framework Class Library, all classes related to multi-thread mechanism applications are placed in system.threaming namespace. The Thread class is provided for creating threads, threadpool classes for managing thread pools, etc., and there is also a mechanism to solve the actual problems such as thread implementation arrangements, deadlocks, and thread communication. If you want to use multithreading in your application, you must include this class. There are several vital methods that the Thread class is described below:
Start (): Start the thread Sleep (int): Static method, suspend the number of milliseconds specified by the current thread (): Usually use this method to terminate Suspend (): This method does not terminate unfinished threads, it only hangs only The thread can be restored later. Resume (): Restore the execution of thread hangs hangs by the suspend () method
1
Below we will do it, create a thread and use the Thread class to create a thread, just provide a thread entry. The thread entry makes the program let this thread do something, in C #, the thread entry is provided through the ThreadStart Agent (DELEGATE), you can understand the ThreadStart as a function pointer, pointing to the thread to be executed, when calling Thread. After the start () method, the thread began executing the function indicated by ThreadStart or points. Open your vs.net, create a console application, the following code will make you feel the endless pleasure of completely controlling a thread!
//ThreadTest.cs using System; using System.Threading; namespace ThreadTest {public class Alpha {public void Beta () {while (true) {Console.WriteLine ( "Alpha.Beta is running in its own thread.");} }}; Public class simple {public static int main () {Console.Writeline ("thread start / stop / join sample"); alpha oalpha = new alpha (); file: // Create a thread to enable Alpha Beta () method thread osread = new thread (new threadstart (oalpha.beta)); osread.start (); while (! Ketread.isalive); thread.sleep (1); osread.abort (); orthread. Join (); console.writeline (); console.writeline ("Alpha.beta Has Finished"); Try {consocle.writeline ("try to restart the alpha.beta thread");} catch (); THREADSTATEEXCEPTION) {Console.write ("ThreadStateException Trying to restart alpha.beta."); Console.writeline ("Expected Since Aborted."); Console.readline ();} return 0;}}} This program contains two classes of Alpha and Simple, when you create thread Othread, we use the pointing alpha. The Beta () method is initialized the ThreadStart Agent (DELEGATE) object, when we created thread oscart () method is started, the actually run is an alpha.beta () method:
Alpha oalpha = new alpha (); thread osread = new thread (new threadstart (oalpha.beta)); osread.start ();
Then in the While cycle of the main () function, we use static methods thread.sleep () to stop the main thread to stop 1ms, this time the CPU turns to execute thread Othread. Then we tried to terminate the thread osread.Join (), the thread.join () method for the main thread until the main thread is over. You can specify an INT type parameter as the longest time waiting for the thread.join () method. After that, we tried to restart the thread oscart () method, but obviously the consequences of the Abort () method are unrecoverable termination threads, so the last program will throw the ThreadStateException. The final result of the program will be as follows: Here we have to note that other threads are considering the thread where the main () function is located, the main () function is the entry of the C # program, the starting thread can be called the main thread If all the front desk threads are stopped, the main thread can be terminated, while all the background threads will be terminated unconditionally. Although all threads are executed in micro, you can do it in a macro, you can do it in parallel. The reader must pay attention to this attribute of thread.threadState. This property represents a thread run state, there is a different value in different cases, so we can sometimes design the program process by judging the value. ThreadState may take the value in various situations:
Aborted: The thread.abort () method of the thread has been called, but the thread has not stopped background: thread executes in the background, with attribute thread.isbackground For Running: Thread is running Stopped: Thread has been stopped StopRequested : The thread is being required to stop suspended: thread has been suspended (this state can be re-run by calling the resume () method) SuspendRequested: thread is required to be suspended, but the future and response unstearted: unused thread.Start () Start thread running Waitsleepjoin: Threads are in a blockade state because Wait (), SLEEP () or JOIN () is called.
The background status is mentioned above indicates that the thread runs in the background, then what special place is there? In fact, the background thread has only one difference with the front desk thread, that is, the background thread does not hinder the termination of the program. Once all the front desk threads are terminated, the CLR (Universal Language Run Environment) will completely terminate the process by calling the Abort () method of any background process in any survival. When the CPU time is competed between the thread, the CPU is served in accordance with the priority of the thread. In the C # application, users can set up five different priorities, from high to low, respect, ABOVENORMAL, NORMAL, BELOWNORMAL, LOWEST, if they do not specify priority when creating a thread, then the system is threadpriority.normal . Give a thread to specify a priority, we can use the following code:
// Set the priority to the lowest mythread.priority = threadpriority.lowest;
By setting the priority of the thread, we can arrange some relatively important threads priority, such as responding to the user, and the like. Now we have a preliminary understanding of how to create and control a thread. Here we will in-depth research of thread implementation, and explore its solution. Third. Synchronization and communication - producers and consumers assume such a situation, two threads maintain a queue while adding elements to the queue, and another thread takes the elements from the queue, then we The thread that adds an element is the producer, called the thread of the elements as consumers. Producers and consumers look simple, but it is a problem that must be solved in multi-threaded applications, which involves synchronization and communication issues between threads. As mentioned earlier, each thread has its own resources, but the code area is shared, ie each thread can perform the same function. However, in multi-threaded environments, problems that may bring are a few threads simultaneously execute a function, resulting in confusion of data, resulting in unpredictable results, so we must avoid this happening. C # provides a keyword Lock that defines a code as a critical section, and the mutual exclusion section only allows a thread to enter execution, while other threads must wait. In C #, the keyword Lock is defined as follows: Lock (Expression) Statement_block
Expression represents an object you want to track, usually an object reference. Generally, if you want to protect an instance of a class, you can use this; if you want to protect a static variable (such as the mutually exclusive code segment in a static method), you can usually use the class name. STATEMENT_BLOCK is the code of the mutual exclusion, which is only performed by one thread in one moment. Below is a typical example of using the Lock key, I will explain to you to you usage and use of the Lock keyword:
//lock.cs using System; using System.Threading; internal class Account {int balance; Random r = new Random (); internal Account (int initial) {balance = initial;} internal int Withdraw (int amount) {if ( Balance <0) {file: // If Balance is less than 0, it throws anomalous throw new exception ("Negative Balance");} // The following code guarantees that there will be no other thread before the current thread modifies the value of Balance. This code is also executed to modify the value of Balance // therefore, the value of Balance is a Lock (this) {Console.Writeline ("Current Thread: Thread.currentthRead]; if: // If Without the protection of the lock keyword, it is possible to perform the value of Balance = Balance-Amount to modify the value of Balance = Balance-Amount to modify the value of Balance = Balance-Amount to modify the value of Balance: //. Therefore, it may result in the conditions of the IF, but the thread continues to execute Balance = Balance-Amount, so the Balance may be less than 0 if (Balance> = Amount) {thread.sleep (5) Balance = Balance - Amount; Return Amount;} else {return 0; // Transaction rejected}}} INTERNAL VOID Dotransactions () {for (int i = 0; i <100; i ) withdraw (R.Next (-50) , 100));}} intern Al class Test {static interface [] threads = new thread [10]; public static void main () {Account ACC = New Account (0); for (int i = 0; i <10; i ) {thread t = New ThreadStart (ACC.DOTRANSACTION); threads [i] = t;} for (int i = 0; i <10; i ) threads [i] .Name = i.toString (); for (int i = 0; i <10; i ) threads [i] .start (); console.readline ();}}
When multi-threaded public use, there will be problems similar to public code. This problem should not be used with the lock key, here you need to use a class Monitor in system.thread, we can call it a monitor. Monitor provides a solution that makes thread sharing resources. The Monitor class can lock an object, and a thread only gets this lock to operate the object. The object lock mechanism guarantees that only one thread can access this object when it may cause confusion. Monitor must be associated with a specific object, but because it is a static class, it cannot be used to define objects, and all of its methods are static, and objects cannot be used. The following code illustrates the situation of using Monitor to lock an object: ... Queue oqueue = new queue (); ...... Monitor.Enter (oqueue); ... // Now oqueue object Monitor.exit (oqueue) can only be manipulated by the current thread; // Release lock
As shown above, when a thread calls the Monitor.Enter () method to lock an object, this object returns all, other threads want to access this object, only waiting to use the Monitor.exit () method to release the lock. In order to ensure that the thread can eventually release the lock, you can write the monitor.exit () method in the finally code block in the try-catch-finally structure. For any object locked by Monitor, some information related to it is saved. One is now a reference to the lock thread, and the other is a preparatory queue, saved in the queue is ready to get the lock. Threads, the third is a queue that is waiting for queue, and saves a queue that is currently waiting for this object status change. When you have an object lock thread ready to release the lock, it uses the Monitor.pulse () method to notify the first thread in the queue, so that the thread is transferred to the preparatory queue, when the object lock is released, in the preparatory queue The thread can get an object lock immediately. Below is an example showing how to use the Lock keyword and the Monitor class to implement thread synchronization and communication, is also a typical producer and consumer problem. In this routine, the producer thread and the consumer thread are alternate. The producer writes a number, the consumer is immediately read and displayed, and I will introduce the essence of the program in the comment. The system namespace used is as follows:
Using system; using system.threading;
First, we define a class CELL of an object that is operated. In this class, there are two ways: readFromcell () and WriteTocell. The consumer thread will call ReadFromcell () to read the contents of CellContents and display it, and the producer process will call the Writtocell () method to write data to CellContents.
Public class cell {int cellcontents; // The content Bool Readerflag = false; // status flag is read when TRUE is written, and it is written to PUBLIC INTROMCELL () {Lock (this) // Lock The keyword guarantees what, please see the introduction of the left to the Lock {if (! Readerflag) // If you don't read {Try {file: // Wait to call Monitor.pulse () Method Monitor.Pulse () Monitor.wait ( this);} catch (SynchronizationLockException e) {Console.WriteLine (e);} catch (ThreadInterruptedException e) {Console.WriteLine (e);}} Console.WriteLine ( "Consume: {0}", cellContents); readerFlag = False; file: // Reset the ReaderFlag flag, indicating that the consumption behavior has completed Monitor.pulse (this); file: // Notify the Writtocell () method (this method is executed in another thread, waiting)} Return CellContents; Public Void WriteTocell (INT N) {if (Readerflag) {Try {Monitor.wait (this);} catch (synchronizationlockexception e) {file: // When synchronization method (referring to the Monitor class except ENTER Method) Call console.writeline (e) in the non-synchronous code area;} catch (ThreadInterruptedExce) Ption e) {file: // When thread is waiting state, console.writeline (E) is stopped when the thread is waiting state;}} CellContents = N; console.writeline ("Produce: {0}", CellContents; Readerflag = true; monitor. Pulse (this); file: // Inform another READFROMCELL () method}}}}}}}}}}}}}}}}}}}} The following is only one method threadrun () so that in the main () function A ThreadStart proxy object that is provided to the thread is provided as the entrance to the thread.
Public class cellprod {cell cell; // is operated Cell object int Quantity = 1; // producers production number, initialization is 1 public cellprod (cell box, int request) {// constructor Cell = Box; Quantity = Request Public void threadrun () {for (int Looper = 1; Looper <= quantity; looper ) Cell.WritTocell (Looper); file: // Writing information to the operation object}} public class cellcons {Cell Cell; int quantity = 1; public CellCons (Cell box, int request) {cell = box; quantity = request;} public void ThreadRun () {int valReturned; for (int looper = 1; looper <= quantity; looper ) valReturned = cell .Readfromcell (); // consumers read information from the operation object}}} and then we have to do in the MAIN () function below this class is to create two threads as producers and consumers, using CellProd. The ThreadRun () method and the cellcons.threadrun () method operates the same CELL object.
Public class monitorsample {public static void main (string [] args) {int result = 0; file: // a flag, if it is 0 indicates that the program does not have an error, if it is indicated that there is an error occurs Cell Cell = New Cell () ; // The following uses Cell initialization CellProd and CellCons two class, production and consumption times are CellProd Prod = New CellProd (Cell, 20); cellcons cons = new cellcons (cell, 20); thread product = new thread New ThreadStart (PROD.THREADRUN); Thread Consumer = New Thread (New ThreadStart (cons.threadrun); // Producer thread and consumer thread have been created, but did not start executing try {product (Start (); Consumer.Start (); consumer.join (); console.join (); console.readline ();} catch (threadStateException E) {file: // When thread is not possible to perform the requested operation Console.writeline (e); result = 1;} catch (threadInterruptedException E) {file: // When thread is in a waiting state, console.Writeline (E); Result = 1;} // Although the main () function is not Return the value, but the following statement can return to the parent process environment.exitcode = result;}} You can see that in the above routine, synchronization is done by waiting for Monitor.pulse (). First, the producer produces a value, while the consumer is waiting for a state until the consumer is notified, after the production is completed, after the consumer enters the consumer state, and the producer begins waiting for consumers to complete "Pulse" issued will be called after the operation. Its execution is very simple:
Produce: 1 Consume: 1 Produce: 2 Consume: 2 Product: 3 Consume: 3 ... Product: 20 Consume: 20
In fact, this simple example has helped us solve the problem that may arise in multi-thread applications, just comprehend the basic method of resolving the conflict between threads, it is easy to apply it to a more complex program. Fourth, thread pool and timer - automatic management of multi-threads In multi-threaded programs, two situations often occur. In one case, the thread in the application takes most of the time in the waiting state, waiting for an event to occur, and then give response; and another case is that the thread is usually in sleep, only periodically wake. In .NET Framework, we use ThreadPool to deal with the first case, use Timer to deal with the second case. The ThreadPool class provides a thread pool maintained by the system-can see a container for a thread, which requires a system support for Windows 2000 or more, because some of which calls only high version of Windows API functions. You can use the ThreadPool.QueueUserWorkItem () method to place the thread in the thread pool, the prototype of this method is as follows: // put a thread into the thread pool, the start () method of the thread calls the WaitCallback proxy object represented by the WaitCallback agent object public static Bool QueueUserWorkItem (WaitCallback); // Overloaded method, the parameter Object will pass to WaitCallback represented by public static bool queueuserworkItem (Waitcallback, Object); It should be noted that the ThreadPool class is also a static class, you can't It is necessary to generate it object, and once the method has added a project in the thread pool, then the item will not be canceled. Here you don't have to build a thread yourself, just write a function you want to do, then pass it as a parameter to the threadpool.QueueUserWorkItem () method, the method of passing is to rely on the WaitCallback proxy object, and the thread is established, managed, and run Waiting for the system automatically, you don't need to consider those complex details, the advantages of the thread pool are also reflected here, as if you are the company boss - just arrange your work without having to do your hands. The following routines demonstrate the usage of ThreadPool. First, a manualReveTevent object is created. This object is like a signal, which can be used to notify other threads. In this example, when all threads in the thread pool are completed, the object of ManualRetevent will be set to have a signal, thus Notify the main thread to continue to run. It has several important methods: reset (), set (), waitone (). When you initialize the object, the user can specify its default state (signal / no signal), after initialization, the object will keep the original state unchanged until its reset () or set () method is called, reset () The method is set to no signal state, and the set () method sets it to have a signal state. Waitone () method The current thread hangs until the ManualReteVent object is in a signal state, and the thread will be activated. The program will then add a work item to the thread pool, which is used by the system to initialize the automatically established thread. When all the threads are running, the ManualReventEvent.set () method is called because the manualReveTevent.Waitone () method is called, and the main thread in the waiting state will receive this signal, so it will be executed down, after completion work.
Using system; using system.threading; // This is the data structure used to save information, which will be transmitted as a parameter PUBLIC CLASS SOMESTATE {PUBLIC INT Cookie; PUBLIC SOMESTATE (IC iCookie) {cookie = iCookie;} } public class Alpha {public Hashtable HashCount; public ManualResetEvent eventX; public static int iCount = 0; public static int iMaxCount = 0; public Alpha (int MaxCount) {HashCount = new Hashtable (MaxCount); iMaxCount = MaxCount;} file: / The thread in the / thread pool will call the Beta () method public void beta (Object State) {// output the current thread's HASH coding value and the value of the cicketole.writeline ("{0} {1}:", thread.currentthread .Gethashcode (), ((Somestate) State); Console.Writeline ("Hashcount.count == {0}, thread.crentthread.gethashcode () == {1}", Hashcount.count, Thread.CurrentThread .Gethashcode (); lock (havehcount) {file: // If there is no current thread in the current Hash table, add IF (! Hashcount.containskey (thread.countthread.gethashcode ())) Hashcount.Add (Thread.currentthread.gethashcode (), 0); hashcount [thread.curre NTTHREAD.GETHASHCODE ()] = ((int) Hashcount [thread.currentthread.gethashcode ()]) 1;} int = 2000; thread.sleep (ix); //interlocked.increment () operation is an atomic operation Please see the interlocked.increment (ref iCount); if (iCount == iMaxcount); console.writeline (); eventx.set ();}}} public class SimplePool {Public static int main (string [] args) {console.writeline ("Thread pool sample:"); BOOL W2K = false; int maxcount = 10; // Allows running up to 10 threads in the thread // New ManualResetEvent object And initialized the signal-free state manualRevent EventX = new manualReveTevent (false);
Console.writeline ("Queuing {0} items to three pool", maxcount); alpha oalpha = new alpha (maxcount); File: // Create a work item / / Note initialization Oalpha object EventX attribute oalpha.eventx = EventX; console .Writeline ("Queue To Thread Pool 0"; try {file: // Loads the work item into the thread pool File: // This will use the API for Windows 2000 or more, so you may have a NotSupportexception exception ThreadPool.queueUserWorkItem (New Waitcallback (Oalpha.beta), New Somestate (0)); w2k = true;} Catch (notsupportedException) {Console.writeline ("The API's May Fail When Called On A Non-Windows 2000 System."); W2K = False;} if (w2k) // If the current system supports ThreadPool method. {for (int IItem = 1; IIITEM Some small places in the program should cause our attention. The SomeState class is a data structure that holds information. In the above program, it is passed to each thread as a parameter, you can easily understand this, because you need to package some useful information to provide a thread, and this The way is very effective. The Interlocked class that appears is also for multi-threaded programs. It provides some useful atomic operations. The so-called atomic operation is in multi-threaded program, if this thread calls this operation to modify a variable, then other threads cannot be modified. This variable is, this is the same as the LOCK keyword is essentially. We should completely analyze the above procedures, grasp the nature of the thread pool, understand what it exists, so that we can use it hand. The following is the output of the program: thread pool sample: queuing 10 items to thread pool Queue to thread pool 0 Queue to Thread pool 1 ... Queue to Thread Pool 9 Waiting for Thread Pool To Drain 98 0: Hashcount. Count == 0, thread.currentthread.gethashcode () == 98 100 1: hashcount.count == 1, thread.currentthread.gethashcode () == 100 98 2: ... Setting Eventx thread pool HAS BEEN Drained (Event Fired) load Across Threads 101 2 100 3 98 4 102 1 Unlike the ThreadPool class, the Timer class's role is to set a timer that performs the user specified by the user, and the transfer of this function is depends on another proxy object TimalLBack, which must be specified when creating the Timer object and cannot be changed. After the timer is started, a new thread will be automatically created and the user specified in this thread. The following statement initializes a Timer object: Timer Timer = New Timer (TimerDelegate, S, 1000, 1000); The first parameter specifies the TimerCallback proxy object; the meaning of the second parameter is the same as the WaitCallback proxy object mentioned above, and the method of transmitting data is passed to the method to be called; the third parameter is delay time - Timing At the beginning of the moment, the unit is millisecond; the fourth parameter is the time interval of the timer - after the timing start, the method represented by TimerCallback will be called once, and the unit is also milliseconds. The meaning of this sentence is to set the delay time and time interval of the timer to 1 second. The setting of the timer can be changed, as long as the Timer.Change () method is called, this is a method of using a parameter type overload, the general use of the prototype is as follows: Public Bool Change (long, long); The following code modifies the timer set in front: Timer.change (10000, 2000); Obviously, the time interval of the timer Timer is reset to 2 seconds, and it will take effect after 10 seconds. The following program demonstrates the usage of the Timer class. using System; using System.Threading; class TimerExampleState {public int counter = 0; public Timer tmr;} class App {public static void Main () {TimerExampleState s = new TimerExampleState (); // create a proxy object TimerCallback, the agent TimerCallback TimerDelegate = New TimalCallback (Checkstatus); // Create a timer Timer Timer Timer Timer Timer Timer Timer Timer (TimerDelegate, S, 1000, 1000); S. Tmr = Timer; // Main Circuit Waiting for the Timer object to stop while (S.TMR! = Null) thread.sleep (0); console.writeline ("Timer Example Done."); Console.readline ();} file: // below is scheduled to be called The method of static void CheckStatus (Object state) {TimerExampleState s = (TimerExampleState) state; s.counter ; Console.WriteLine ( ". {0} Checking Status {1}", DateTime.Now.TimeOfDay, s.counter); if ( S.counter == 5) {file: // Use the Change method to change the time interval (S.TMR) .change (10000, 2000); console.writeline ("Changed ...");} if (S.Counter == 10) {Console.WriteLine ("Disposing of Timer ..."); S.TMR.Dispose (); S.TMR = NULL; }}} The program first created a timer, which will call the checkstatus () method every 1 second after creating 1 second, and after the call 5 times, the time interval is modified in the checkstatus () method for 2 seconds. And designated after 10 seconds. When the count reaches 10 times, call the Timer.dispose () method to delete the Timer object, the main thread then jumps out the loop and terminates the program. The result of the program execution is as follows: The above is a brief introduction to ThreadPool and Timer, making full use of the features provided by the system, which can save us a lot of time and energy - especially for multi-threaded programs that are easily erroneous. At the same time, we can also see the powerful built-in objects of .NET Framework, which will bring more convenient to our programming. Mutually exclusive object - more flexible synchronization mode sometimes you will feel that the method described above seems to be insufficient, and we solve the synchronization problem of code and resources, solve the problem of multi-threaded automation management and timed trigger, but how Control the connection between multiple threads? For example, I want to eat to the restaurant. I first wait to wait for the chef to do a good job before eating. After I started eating, I have to pay, I have to pay, and the payment method can be cash, or I can leave after payment. Analyze this process, I can see it as a main thread. The chef cook is a thread. The waiter uses credit card collection and collecting cash to see two threads. You can clearly see the relationship - I have to wait for the chef to cook, then wait for any of the two receiving threads, then I have to eat this thread can perform leaving this step, so I have an end. In fact, there is more complex contact than this, how can we control them well without conflict and repeat? In this case, we need to use the mutex class in the system.threaming namespace. Everyone must take a taxi. In fact, we can think of Mutex as a taxi, then passengers are threads, passengers must wait for the car, then get on the bus, and finally get off, when a passenger is on the car, other passengers Just wait for him to get off the bus. The relationship between thread and mutex object is also the case, the thread is waiting to be released using the MUTEX object, if it waitsible Mutex objects, it automatically owns this object until it calls Mutex.ReleaseMutex ( The method releases this object, and during this time, other threads that want to get this Mutex object are only waiting. The following example uses the Mutex object to synchronize four threads, the main thread waits for the end of the four threads, and the operation of the four threads is associated with two MUTEX objects. Among them, the object of the AutoreteTevent class is also used as the ManualRestEvent object mentioned above. You can simply understand it as a signal, using the AutoreteTevent.Set () method to set it as a signal state, and use AutoreteTevent.Reset The method is set to have no signal. Here is the end of a thread with its signal state. // Mutex.cs using System; using System.Threading; public class MutexSample {static Mutex gM1; static Mutex gM2; const int ITERS = 100; static AutoResetEvent Event1 = new AutoResetEvent (false); static AutoResetEvent Event2 = new AutoResetEvent (false) ; static AutoResetEvent Event3 = new AutoResetEvent (false); static AutoResetEvent Event4 = new AutoResetEvent (false); public static void Main (String [] args) {Console.WriteLine ( "Mutex Sample ..."); // create a Mutex Object, and named mymutex gm1 = new mutex (true, "mymutex"); // Create a unnamed Mutex object. GM2 = new mutex (true); console.writeline ("- main owns gm1 and gm2); AutoreteTevent [] EVS = New AutoRetevent [4]; EVS [0] = Event1; File: // For the rear threads T1, T2, T3, T4 Define AutoreseTevent Object EVS [1] = Event2; EVS [2] = Event3; evs [3] = Event4; MutexSample tm = new MutexSample (); Thread t1 = new Thread (new ThreadStart (tm.t1Start)); Thread t2 = new Thread (new ThreadStart (tm.t2Start)); Thread t3 = new Thread (New ThreadStart (TM .t3start); Thread T4 = New Thread (New ThreadStart (TM.T4Start); T1.Start (); // Wait for a MUTEX.Waitall () method Wait all objects in a MUTEX array to be released T2.Start () ; // Wait for the release of GM1 to release T3.Start (); // using the Mutex.Waitany () method to wait for any object (); // using Mutex. Waitone () method Wait for GM2 to release thread.sleep (2000); console.writeline ("- main release"); gm1.releasemutex (); file: // thread T2, T3 end condition satisfies thread.sleep (1000) Console.writeline ("- main releases"); gm2.releasemutex (); file: // thread T1, T4 end condition satisfies // Waiting for all four threads end Waithandle.Waitall (EVS); console.writeline ... mutex sample "); console.readline (); PUBLIC VOID T1START () {Console.Writeline ("T1Start Started, Mutex.waitall (Mutex [])"); Mutex [] GMS = New Mutex [2]; GMS [0] = GM1; // Create a MUTEX array As a Mutex.Waitall () method, parameter GMS [1] = GM2; Mutex.waitall (GMS); // Waiting for GM1 and GM2 to be released Thread.Sleep (2000); console.writeline ("T1start Finished, Mutex.Waitall (Mutex []) Satisfied "); event1.set (); file: // thread ends, set Event1 to have signal status} public void t2start () {Console.writeline (" T2Start Started, GM1.Waitone () " ); Gm1.waitone (); // Waiting for GM1 release console.writeline ("T2Start Finished, GM1.Waitone () Satisfied"); Event2.Set (); // The thread ends, set Event2 to have signal status} Public void t3start () {console.writeline ("T3Start Started, Mutex.waitany)"); Mutex [] GMS = New Mutex [2]; GMS [0] = GM1; // Create a MUTEX array as Mutex.waitany () method of parameter GMS [1] = GM2; Mutex.waitany (GMS); // Wait for any Mutex object in array to release Console.WriteLine ("T3Start Finished, Mutex.Waitany)" ); Event3.SET (); // thread end, set Event3 to have signal status} public void t4start () {Console.WriteLine ("T4 START STARTED, GM2.WAITONE () "); gm2.waitone (); // Waiting for GM2 to be released Console.Writeline (" T4Start Finished, GM2.Waitone () "); Event4.Set (); // end, Set Event4 to have signal status}} The following is the execution result of the program: As can be seen from the execution result, the operation of threads T2 and T3 is the release of GM1, while T4 begins after GM2 release, T1 is released after GM1 and GM2 are released. MAIN () function Finally, using WaitHandle Waiting for the signal of all AutoResetEvent objects, the signal of these objects represents the end of the corresponding thread. 6. Small junction multithreading program is a huge topic, and this article tries to describe the overview of the multi-threaded program in the .NET Framework environment. I hope this article can help you understand this concept of threads, understand the use of multi-threads, understand its C # implementation method, understand the benefits and troubles of our thread will bring us. C # is a new language, so its thread mechanism has many unique places, I hope that everyone can see these in this article, so that threads can be more in-depth understanding and exploration.