Note: Code All run through. NET Framework RC3
I. multithreading Concept
Windows is a multitasking system. If you are using Windows 2000 or later, you can view the programs and processes running on the current system through the task manager. What is a process? When Program When running, a process refers to the memory and system resources used by running programs and programs. A process is composed of multiple threads. A thread is an execution stream in a program, and each thread has its own proprietary register (Stack pointer, program counter, etc ), but the code zone is shared, that is, different threads can execute the same function. Multithreading means that a program contains multiple execution streams, that is, a program can run multiple different threads to execute different tasks at the same time, that is to say, a single program is allowed to create multiple parallel threads to complete their respective tasks. A browser is a good example of multithreading. In a browser, you can scroll the page while downloading a Java application or image, and play animations and sounds when accessing a new page, print files.
The advantage of Multithreading is that it can improve the CPU utilization. Any programmer does not want his or her program to work normally. In a multithreaded program, a thread must wait, the CPU can run other threads instead of waiting, which greatly improves the program efficiency.
However, we must also recognize the negative aspects that the thread itself may affect system performance to use the thread correctly:
- The thread is also a program, so the thread needs to occupy the memory. The more threads occupy the memory, the more
- Multithreading requires coordination and management, so it requires CPU time to track threads.
- Inter-thread access to shared resources will affect each other, and the problem of competing to share resources must be solved.
- Too many threads will lead to complicated control, and may eventually cause many bugs.
Based on the above understanding, we can use a metaphor to deepen our understanding. Suppose there is a company with many employees with their respective duties, then we can think that this normal operating company is a process, and the staff in the company is a thread. A company must have at least one employee. Similarly, a process must contain at least one thread. In a company, you can do everything for one employee, but the efficiency is obviously not high. A company cannot do anything bigger. In a program, you can use only one thread to do things, in fact, this is true for outdated languages such as fortune and basic, but like a company, it is very inefficient. If it is a large program, it is less efficient-in fact, there is almost no single-threaded commercial software. The more employees the company has, the more salaries the boss has to give to them, and it has to spend a lot of energy to manage them and coordinate the contradictions and interests between them. The same is true for the program, the more threads consume more resources, the CPU time is required to track the threads, and problems such as deadlocks and synchronization must be solved. In short, if you don't want your company to be called a "bag company", you will have a few more employees. If you don't want your program to look childish, introduce multithreading in your program!
This article will discuss the multithreading mechanism in C # programming and solve the thread control and multi-thread communication issues through some examples. In order to save the tedious steps for creating a GUI and more clearly approach the essence of the thread, all the following programs are console programs and the final console of the program. readline () is used to stop the program midway through, so that you can see the output in the execution process clearly.
Okay, let's try out the multi-threaded C!
2. manipulate a thread
When any program is executed, there must be at least one main thread. The following small program can give readers an intuitive impression:
// Systemthread. CS Using system; Using system. Threading;Namespace threadtest { Class Runit { [Stathread] Static void main (string [] ARGs) { Thread. currentthread. Name = "system thread"; // name the current thread "system thread" Console. writeline (thread. currentthread. Name + "'status:" + thread. currentthread. threadstate ); Console. Readline (); } } } |
What do you see after compilation and execution? Yes, the program will generate the following output:
System thread's status: Running
Here, we get the thread currently being executed through the static attribute currentthread of the thread class, assign the value "system thread" to its name attribute, and finally output its current state (threadstate ). The so-called static attribute is the public attribute of all objects in the class. No matter how many instances of the class you have created, the static attribute of the class has only one in the memory. It is easy to understand why currentthread is static-although multiple threads exist at the same time, the CPU can only execute one of them at a certain time point.
As demonstrated by the above program, we use the Thread class to create and control threads. Note that the program header uses the following namespace:
Using system; Using system. Threading; |
In. NET Framework class library, all classes related to multithreading applications are stored in the system. Threading namespace. The thread class is used to create threads, and the threadpool class is used to manage thread pools. In addition, it provides a mechanism to solve actual problems such as thread execution arrangements, deadlocks, and inter-thread communication. If you want to use multithreading in your application, you must include this class. The thread class has several important methods, which are described as follows:
- Start (): Start the thread
- Sleep (INT): a static method that pauses the specified number of milliseconds of the current thread.
- Abort (): This method is usually used to terminate a thread.
- Suspend (): This method does not terminate the unfinished thread. It only suspends the thread and can be recovered later.
- Resume (): resume the execution of threads suspended by the suspend () method
Next we will create a thread. When using the Thread class to create a thread, we only need to provide the thread entry. The thread entry tells the program what to do with this thread. in C #, the thread entry is provided through the threadstart proxy (delegate). You can regard threadstart as a function pointer, point to the function to be executed by the thread. after the start () method, the thread starts to execute the Functions Represented or pointed to by threadstart.
Open your vs.net and create a console application. The following code will give you the pleasure of fully 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 execute beta () of the Alpha Class () method thread othread = new thread (New threadstart (oalpha. beta); othread. start (); while (! Othread. isalive); thread. sleep (1); othread. abort (); othread. join (); console. writeline (); console. writeline ("Alpha. beta has finished "); try {< br> console. writeline ("try to restart the Alpha. beta thread "); othread. start (); }< br> catch (threadstateexception) {< br> console. write ("threadstateexception trying to restart Alpha. beta. "); console. writeline ("expected since aborted threads cannot be restarted. "); console. readline (); }< br> return 0; }< BR >} |
This program contains two classes: Alpha and simple. when creating the thread othread, we use the pointer to Alpha. the threadstart proxy (delegate) object is initialized in the beta () method. When the created thread othread calls othread. when the START () method is started, the actual program running is Alpha. beta () method:
Alpha oalpha = new alpha (); Thread othread = new thread (New threadstart (oalpha. Beta )); Othread. Start (); |
Then in the while loop of the main () function, we use the static method thread. Sleep () to stop the main thread for 1 ms. During this time, the CPU turns to the execution thread othread. Then we try to use the thread. Abort () method to terminate the thread othread. Pay attention to the following othread. Join (), thread. Join () method to wait for the main thread until the othread thread ends. You can specify an int-type parameter for the thread. Join () method as the maximum waiting time. Later, we tried to use the thread. Start () method to restart the thread othread, but obviously the consequence of the abort () method is that the thread cannot be recovered, so the program will throw a threadstateexception.
The final result of the program will be as follows:
Note that other threads are attached to 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 foreground threads are stopped, the main thread can be terminated, and all background threads will be terminated unconditionally. While all threads are executed in a serial way at the micro level, you can think of them as being executed in parallel at the macro level.
The reader must have noticed the thread. threadstate, which represents the state of a thread during runtime and has different values under different circumstances. Therefore, we can design a program flow by judging the value. Threadstate may take the following values in various situations:
- Aborted: the thread has stopped.
- Abortrequested: The thread. Abort () method of the thread has been called, but the thread has not stopped.
- Background: The thread is executed in the background, which is related to the attribute thread. isbackground.
- Running: The thread is running normally.
- Stopped: the thread has been stopped.
- Stoprequested: The thread is being requested to stop
- Suincluded: the thread has been suspended. (In this status, you can call resume () to run the thread again)
- Suspendrequested: The thread is requesting to be suspended, but cannot respond.
- Unstarted: thread. Start () is not called to start the thread.
- Waitsleepjoin: The thread is blocked because it calls methods such as wait (), sleep (), and join ().
As mentioned above, the background State indicates that the thread is running in the background. What are the special features of the backend running threads? In fact, there is only one difference between the background thread and the foreground thread, that is, the background thread does not prevent program termination. Once all foreground threads of a process are terminated, CLR (general language runtime environment) will completely terminate the process by calling the abort () method of any surviving background process.
When the threads compete for the CPU time, the CPU is given the service according to the priority of the thread. In the C # application, you can set five different priorities, from high to low, which are highest, abovenormal, normal, belownormal, and lowest. If the priority is not specified during thread creation, the default value is threadpriority. normal. Specify a priority for a thread
You can use the following code:
// Set the priority to the lowest Mythread. Priority = threadpriority. Lowest; |
By setting the thread priority, we can arrange some important threads for priority execution, such as user response.
Now we have a preliminary understanding of how to create and control a thread. Next we will study the typical problems in thread implementation in depth and discuss the solution.
III.Synchronization and communication of threads-producer and consumer
Assume that two threads maintain a queue at the same time. If one thread adds an element to the queue and the other thread uses the element from the queue, we call the thread for adding elements as the producer, and the thread for using elements as the consumer. The producer and consumer problems seem simple, but they are a problem that must be solved in multi-threaded applications. They involve synchronization and communication between threads.
As mentioned above, each thread has its own resources, but the code zone is shared, that is, each thread can execute the same function. However, in a multi-threaded environment, the possible problem is that several threads execute a function at the same time, resulting in data confusion and unexpected results. Therefore, we must avoid this situation. C # provides a keyword lock, which defines a piece of code as a critical section. A mutex section allows only one thread to enter the execution at a time point, other threads must wait. In C #, the keyword lock is defined as follows:
lock (expression) statement_block |