Preliminary study on multi-thread mechanism of C #

xiaoxiao2021-03-06  72

I. Multi-threaded concept   Windows is a multi-task system, if you are using Windows 2000 and above, you can view programs and processes running the current system through 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 a execution stream in the program, each thread has its own proprietary register (stack pointer, program counter, etc.), but the code area is shared, that is, different The thread 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 good multi-threaded example. In the browser you can scroll the page while downloading the Java applet or image, play movies and sounds, print files, etc. Multi-threaded benefits can improve the Usage of CPUs - any programmer does not want his own procedure to be dry. In multi-threaded programs, a thread must wait, CPU can run other threads Instead of 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, we can deepen an understanding. Suppose there is a company, there are many staff members in the company, then we can think that the company is a process, and the staff of 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 communication 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.   well, nonsense, let us experience the multi-thread 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 ();}}} After you have seen it? Yes, the program will produce the following output:

System Thread's Status: Running

Here, we got the currently executed thread through the static property of the Thread class, assigning "System Thread" on its Name property, and finally outputs its current state. 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.

Just like the above program, we create and control threads through the Thread class. Note the head of the program, we used the following namespace:

Region; job.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

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 controlling a thread!

                                                     {limited {console.writeline ("alpha.beta is ");}}}; public class simple}}; public class simple};" Thread Start / Stop / Join Sample "; alpha oalpha = new alpha (); file: / / Create a thread here to perform the Beta () method of the alpha class () method thread (new threadstart (oalpha.beta)); orthread.start (); while (! Ketread.isalive); heread.sleep (1); osread.abort (); otherread.join (); console.writeline (); console.writeline ("alpha.beta Has finished"); try {console.writeline ("try to restart the alpha.beta thread "); Ketread.start ();} catch (threadStateException) {console.write (" ThreadStateException Trying to restart alpha.beta); "" Expected Since Aborted. "); Console.readline ();} return 0;}}} This program contains two classes alpha and simple, when you create thread Othread, we use points to Alpha.beta () method Initialize the ThreadStart agent (DELEGATE) object, when we created thread oscart () method is started, the actual program is actually running an alpha.beta () method:

                                            ;;;;;;;;;;;;;

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 result of the final result will be as follows:

Here we have to note that other threads are attached to the thread of the main () function, 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 stopped Then, the main thread can be terminated, and 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. Readers must notice this attribute of Thread.ThreadState. This property represents a thread run state, there is a different value in different situations, 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 above mentioned the Background status indicates that the thread runs in the background, then what is the special place for threads running in the background? 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 thread competes for the CPU time, 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:

                                           m;

By setting the priority of the thread, we can arrange some relatively important threads priority, such as responses to users, and more.

Now we have a preliminary understanding of how to create and control a thread. Below we will in depth, in depth, the thread implementation is more typical, and explore its solution. three.

Thread synchronization and communication - producers and consumers

  assume that two threads maintain a queue while adding an element to the queue, and another thread takes the elements from the queue, then we call the thread of the addition element, weigh the use Element thread is consumer. 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.

  previously said, 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 (Balance <0) {file: // If Balance is less than 0, the exception throw new exception ("Negative Balance");}                                                                                                 } The following code guarantees that before the current thread modifies the value of Balance to complete this code to modify the value of Balance // So, the value of Balance is not less than 0 LLOCK (this) {Console.Writeline ("Current Thread:" Thread.currentthread.name); File: // If there is no Lock keyword protection, it is possible to determine if the IF is executed, file: // A thread has executed Balance = Balance-Amount to modify the value of Balance: // and this modification is invisible to this thread, so it may result in this case that the IF condition is not established File: // However, this thread is Continue to execute Balance = Balance-Amount, so that Balance may be less than 0 if (Balance> = Amount) {Read.Sleep (5);                                  0; // transaction rejected}}}                                                                                                                                      w   Rnal class test [] threads = new thread [10]; public static void main () {Acount ACC = New Account (0); for (int i = 0; i <10; I ) {Thread T = New Thread (New ThreadStart (Acc.dotransactions); threads [i] = t;}                                                                                                                          Name = i.toString (); for (int i = 0; i <10; i ) threads [i] .Start () ;console.readline ();                                                                                                                          When an object, there will be a similar problem with public code. This problem should not be used with the Lock key. You can use a class Monitor in system.thread, we can call Monitor, Monitor provides Make 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 case of using Monitor to lock an object:   queue oqueue = new queue (); ...... Monitor.enter (oqueue); .....// The Oqueue is now only manipulated by the current thread. Monitor.exit (oqueue); // Release lock

As shown above, when a thread calls the Monitor.Enter () method, this object is owned by it, and 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.

The following 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 issues. 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:

Region; job.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 for true, which is written in false, LinLock (this) // Lock key guarantee anything, please look at the front of the lock introduction  {if (readerFlag!) // If we can not read {try  {file: // wait WriteToCell method Monitor.Pulse call () method Monitor.Wait (this); } catch (SynchronizationLockException e)  {Console.WriteLine (e); } catch (ThreadInterruptedException e)  {Console. WriteLine (e);}} console.writeline ("consume: {0}", cellcontent; readerflag = false; file: // Reset ReaderFlag flag, indicating that consumption behavior has completed Monitor.Pulse (this); File : // Notify the WriteTocell () method (this method is performed in another thread, waiting)}                                                                                                                     \ ) {try  {Monitor.Wait (this); } catch (SynchronizationLockException e)  {file: // when synchronization method (refers to a method other than the Enter Monitor class) in unsynchronized Code area is called Console.Writeline (e);   Catch (ThreadInterruptedex CEPTION E) {file: // When thread stops console.writeline (e) when the thread is waiting state;}} CellContents = N; Console.Writeline ("Produce: {0}", CellContents; Readerflag = True; Monitor.pulse (this); File: // Notifies another READFROMCELL () method in the thread                                                                                                                                Threadrun () so that the ThreadStart proxy object of the thread is provided in the main () function as the entrance to the thread.

Public Class CellProd {Cell Cell; // Operated Cell Object INT Quantity = 1; // Producer Production Number, Initialization to 1 Public CellProd (Cell Box, Int Request) { // Construction Function    publicity = request;                                                                                                          writing information} } public class CellCons {Cell cell; int quantity = 1; public CellCons (Cell box, int request) {cell = box; quantity = request;} public void ThreadRun ( {       = quantity; looper ) valreturned = cell.readFromcell (); // Consumer read information from the operation object}} then under this The main () function we have to do in class monitorsample is to create two threads as producers and consumers, using the cellprod.threadrun () method and the cellcons.threadrun () method to operate the same CELL object.

Public Class Monitorsample {Public Static Void Main (String [] args) { in in in = = 0; file: // A flag bit, if it is 0 indicates that the program has no error, if it is indicated that there is an error. Cell Cell = new cell (); // The following uses Cell initialization CellProd and Cellcons two classes, production and consumption times are 20 times CellProd Prod = New CellProd (Cell, 20); Cellcons Cons = New Cellcons (Cell, 20) ;Thread Producer = New Thread (New ThreadStart (PROD.THREADRUN) ;Thread Consumer = New Thread (New ThreadStart (cons.threadrun); // Producer thread and The consumer thread has been created, but did not start to execute   {product (); consumer.Start (); consumer.join (); console.ieadline ();                                          = = 1; {File: // When thread is waiting state, constole.Writeline (E) is stopped; Result = 1;}                                     The parent process returns an execution result                                                                               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: 1Consume: 1Produce: 2 2Consume: 2 2Produce: 3 Consume: 3                                                                                          

In fact, this simple example has helped us solve the problem that may arise in multi-thread applications, just understand the basic method of solving the conflict between thread, it is easy to apply it to a more complex program.

Fourth, thread pool and timer - automatic management of multi-thread

Two cases often occur in multi-threaded programs. 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.

ThhreadPool class provides a thread pool maintained by the system - can see a container for a thread that requires the system support of Windows 2000 or higher, because some methods are called 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:                                       

Public Static Bool QueueUserWorkItem (WaitCallback);

/ / The method of overload is as follows, the parameter Object will be passed to the method represented by WaitCallBack.

Public Static Bool QueueuserWorkItem (Waitcallback, Object);

It is important to note that the ThreadPool class is also a static class, you can't do it, you can't generate it object, and once you use this method to add a project in the thread pool, then the project will have no way to cancel. 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.

                                                                                                                                                   public SomeState (int 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) {hcount = new hashtable (maxcount); } file: // The thread in the thread pool will call the beta () method public void beta (Object State) { // output current Threaded hash coding value and cookie value Console.Writeline ("{0} {1}:" thread.currentthread.gethashcode (), ((Somestate), ((Somestate),        connsole.writeline ("Hashcount.count == {0}, thread.currentthread.gethashcode () == {1}", havehcount.count, thread.currentthread.gethashcode ()) ;Lock (Hashcount) {file: / / If there is no Hash value of the current thread in the current Hash table, add IF (! Hashcount.containskey (thread.count.containskey (thread.cuntthread.get (thread.cursthread.gethashcode (), 0); Hashcount [thread.cur rentThread.GetHashCode ()] = ((int) HashCount [Thread.CurrentThread.GetHashCode ()]) 1; } int iX = 2000; Thread.Sleep (iX);  // Interlocked. Increment () operation is an atomic operation, please see below,                                                                                                                                                                                                                                                                                Bool w2k = false;                                                                 

Console.writeline ("Queuing {0} items to three pool", maxcount);                                                                oAlpha.eventX = eventX; Console.WriteLine ( "Queue to thread pool 0"); try  {file: // will work items into the thread pool file: // here to use than Windows 2000 only version of the API, so abnormalities may occur NotSupportException ThreadPool.QueueUserWorkItem (new WaitCallback (oAlpha.Beta), new SomeState (0)); W2K = true; } catch (NotSupportedException)  {Console.WriteLine ( "These API's may fail when called on a non-Windows 2000 system."); W2K = false;. } if (W2K) // methods if the current system supports the ThreadPool  {for (int iItem = 1 IIITEM

We should completely analyze the above procedures, grasp the essence of thread pool, understand what it exists, so that we can use it hand. Below is the output of the program:                                                                                                                                          u Thread pool 9Waiting for Thread pool to drain                                                                                                           === # .Currentthread.gethashcode () == 100                                             100 38 41102 1

Unlike the ThreadPool class, the role of the Timer class is to set a timer that performs the user's specified function, while the transfer 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 - The timing of the timing is now, 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 timer settings can be changed, as long as the Timer.Change () method is called, this is a method of using a parameter type overload. The prototype usually used 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 Timer Timer is reset to 2 seconds, and it takes effect after 10 seconds of stopping count.

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, which will be scheduled to call TimerCallback TimerDelegate = New TimerCallback (Checkstatus); // Create a time interval to 1S timer Timer Timer = New Timer (TimerDelegate, S, 1000, 1000); S. Tmr = Timer; // Main thread stops waiting for Timer object to termination   WHILE (S.TMR! = null) thread.sleep (0) Console.writeline ("Timer Example Done.") ;Console.readLine ();} File: // The following is the method of timing call static void checkstatus (Object State) {TimeRexampleState s = (TimeRexamplestate) ) ;s.counter ; console.writeline ("{0} checking status {1}.", datetime.now.timeofday, s.counter);                      file: // Change the time interval (S.TMR). Change (10000, 2000); console.writeline ("Changed ...");} }i (S.counter == 10) {Console.WriteLine ("Disposing of Timer ..."); S.TMR.Dispose (); S.TMR = NULL; }}                                                                                                                                                    The time interval is 2 seconds and the specified starts 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, and make full use of the features provided by the system, you can save us a lot of time and effort - 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.

V. Mutually exclusive object - more flexible synchronization mode, sometimes you will feel that the method described above is not enough, and we solve the synchronization of code and resources, solve multithreaded automation management and timing trigger issues. But how do I control 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.threading 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 these 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 Mutex object unnamed gM2 =. New Mutex ("- Main Owns GM1 and GM2");      = new automorest [] evs = new automoretevent [4]; EVS [0] = Event1; file: // Threads T1, T2, T3, T4 Define AutoreseTevent Object   Evs [1] = Event2; Evs [2] = Event3; Evs [3] = Event4; MutexSample TM = New MutexSample ();  Teread T1 = New Thread (New ThreadStart) ;Thread T2 = New Thread (New ThreadStart) ;Thread T3 = New Thread (New ThreadStart) Tm.t3start) ;Thread T4 = New Thread (New ThreadStart (TM.T4Start)); T1.Start (); // Waiting for a Mutex array in a MUTEX array to be released T2.Start (); // Wait for GM1 to release the release of GM1                   被T4.Start (); // Waiting for GM2 release                          File: // Thread T2, T3 End Conditions MeetsThread.Sleep (1000); Console.writeline ("- Main Releases GM2"); GM2.ReleaseMutex (); file: // Thread T1, T4 The end conditions are satisfied                                     

} Public void t1start ()      le.w ("t1start start, mutex.waitall)"); Mutex [] GMS = New Mutex [2]; GMS [0] = GM1; // Create a MUTEX array as a Mutex.Waitall () method parameter                                                     t                                                                              ("T1Start Finished, Mutex.waitall (Mutex []) Satisfied"); Event1.Set (); File: // Thread end, set Event1 to have signal status} public void t2start ) {Console.WriteLine ( "t2Start started, gM1.WaitOne ()"); gM1.WaitOne (); // wait for release of Console.WriteLine gM1 ( "t2Start finished, gM1.WaitOne () satisfied ");                                                            ; Mutex [] GMS = New Mutex [2];                                                                                    .Waitany (GMS); // Wait for any Mutex object in array to be released   convele.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 () ");                                                                                                                                             ; // The thread ends, set Event4 to have signal status}                                                                                                                           

From the execution result, it can be clearly seen that the running T2, T3 is the release of GM1, while T4 is executed after GM2 release, and 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.

Sixth, small knot

Multi-threaded programming is a huge topic, and this article tries to describe the profile of multi-threaded programs 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.

Click here to download all source code.

转载请注明原文地址:https://www.9cbs.com/read-121275.html

New Post(0)