Note: The code that appears in this article is running in the .NET Framework RC3 environment.
One. Multi-threaded concept
Windows is a multitasking system, if you are using Windows 2000 and above, 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 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. when accessing the new page while accessing the new page.
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 threads occupy the memory, the more multi-threads need to coordinate and manage, so the access to the shared resource will be interacting between the CPU time tracking thread thread, and must solve the bidding share resources. The problem thread will cause the 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 #! Second. Manipulate a thread
Any program is executed, at least one main thread, the following small program can give the reader an intuitive impression:
[CODE] //SystemThread.cs using System; using System.Threading; namespace ThreadTest {class RunIt {[STAThread] static void Main (string [] args) {Thread.CurrentThread.Name = "System Thread"; // to the current The thread is named "System Thread" Console.WriteLine (Thread.currentthread.name "'Status:" thread.currentthread.threadState; console.readline ();}}} [/ code]
What did you see after compiling execution? 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. Notice the head of the program, we used the following namespace: The following is the program code:
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 the thread hangs hangs under the Suspend () method. Let's create a thread. When you create a thread using the Thread class, you only need to 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!
// threadtest.cs
{limiteding in ip }}};
Public class simple {public static int main () {Console.WriteLine ("thread start / stop / join sample);
Alpha oalpha = new alpha (); // Create a thread to perform the Beta () method of the Alpha class () method thread osread = new thread (new threadstart (oalpha.beta)); ketread.start (); while (! Ketread .Isalive) ;Thread.Sleep (1); osread.abort (); orthread.join (); console.writeline (); console.writeline ("alpha.beta Has finished"); try {console.writeLine "Try to restart the Alpha.Beta thread"); oThread.Start ();} catch (ThreadStateException) {Console.Write () "ThreadStateException trying to restart Alpha.Beta."; Console.WriteLine ( "Expected since aborted threads can not "); console.readline ();} return 0;}}}
This program contains two classes alpha and simple. When you create thread Othread, we use the initialization of the ThreadStart Agent (DELEGATE) object to the alpha.beta () method, when we create thread oscart () method When starting, actually run the 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. 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. Third. 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
# }
THROW New Exception ("Negative Balance"); ) {Console.WriteLine ( "Current Thread:" Thread.CurrentThread.Name); // if there is no protection key lock Then there may be the value of Balance = Balance-Amount to modify the value of Balance = Balance-Amount to the value of Balance = Balance-Amount. ^ ^ ^ ^ ^ ^ It is not visible to this thread, so it may result in the conditions of IF, but this thread continues to execute Balance = Balance-Amount, so the balance may be less than 0. # for (int i = 0; i <100; i ) Withdraw (r.Next (-50, 100)); } } internal class Test { static interface [] threads = new thread [10]; Public static void main ( } = 0; I <10; I ) }
When multi-thread public use, there will be problems similar to public code. This problem should not be used with the lock key. You can use a class Monitor in system.thread, we can call it 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 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;