On the multithreading mechanism of C #

Source: Internet
Author: User
Tags execution expression final mutex sleep static class thread thread class
Multithreading Note: The code that appears in this article runs through the. NET Framework RC3 Environment

A. The concept of multithreading

windows is a multitasking system, and if you are using Windows 2000 and above, you can view the programs and processes that are running on the current system through Task Manager. What is a process? When a program starts running, it is a process that refers to the memory and system resources used by the running programs and programs. And a process is composed of multiple threads, thread is a flow of execution in the program, each thread has its own proprietary registers (stack pointers, program counters, etc.), but the code area is shared, that is, different threads can perform the same function. Multithreading is a program that contains multiple execution streams in which multiple different threads can be run at the same time to perform different tasks, that is, to allow a single program to create multiple threads executing in parallel to accomplish their tasks. The browser is a good example of multithreading, in the browser you can download Java small application or image while scrolling the page, when accessing new pages, play animation and sound, print files and so on.

The advantage of multithreading is that it can improve CPU utilization--any programmer doesn't want its program to have a lot of time to do, in multithreaded programs, when a thread has to wait, the CPU can run other threads instead of waiting, which greatly increases the efficiency of the program.

However, we must also recognize that the thread itself may affect the adverse aspects of system performance to properly use the thread:

Threads are also programs, so threads need to occupy memory, and more threads consume more memory
Multithreading needs to be coordinated and managed, so CPU time tracking threads are required
Access to shared resources between threads can affect each other, and the issue of competing shared resources must be addressed
Too many threads can cause control to be too complex, and can eventually cause many bugs

Based on the above understanding, we can a metaphor to deepen understanding. Assuming that there is a company with a lot of staff in the company, then we can assume that a functioning company is a process and that the employees in the company are threads. A company must have at least one employee, for the same reason, a process contains at least one thread. In the company, you can do all the work with one employee, but efficiency is obviously not high, a person's company can not be big; A program may also use only one thread to do things, in fact, some outdated languages such as fortune,basic, but like a person's company, is inefficient, If you do a bigger program, it's less efficient--virtually no single-threaded commercial software. The more employees the company has, the more money the boss has to pay them, also have to expend a lot of energy to manage them, reconcile their contradictions and interests; The program is the same, the more resources the more the more the resource needs CPU time to track the thread, but also to solve such as deadlock, synchronization and other issues. In short, if you don't want your company to be called a "shell company," You have to have a few more employees; If you don't want your program to look childlike, introduce multithreading into your program!

This article will discuss the multithread mechanism in C # programming, and solve the problem of thread control and multithreading communication through some examples. In order to eliminate the cumbersome steps of creating the GUI, and to more clearly approximate the nature of the thread, all of the following programs are console programs, and the final console.readline () of the program is to stop the program in order to see the output in the execution process.

Well, less nonsense, let's experience a multithreaded C # bar!
Two. Manipulate a thread

Any program in the implementation of at least one main thread, the following small program can give the reader an intuitive impression:

The following is the program code
SystemThread.cs
Using System;
Using System.Threading;

Namespace ThreadTest
{
Class Runit
{
[STAThread]
static void Main (string[] args)
{
Thread.currentthread.name= "System thread";//named "System thread" to the current thread
Console.WriteLine (thread.currentthread.name+ "' Status:" +thread.currentthread.threadstate);
Console.ReadLine ();
}
}
}


What do you see after compiling the execution? Yes, the program will produce the following output:

system Thread ' s status:running

Here, we get the currently executing thread through the static property CurrentThread of the thread class, assign "System Thread" to its Name property, and finally output its current state (ThreadState). A static property is a property that is public to all objects of this class, regardless of how many instances of this class you create, but the static properties of the class are only one in memory. It's easy to understand why CurrentThread is static--although there are multiple threads at the same time, at a certain point, the CPU can only execute one of them.

As the previous program demonstrates, we create and control threads through the thread class. Noting the head of the program, we used the following namespaces:
The following are program code:

using System;
using System.Threading;



In the. NET Framework class library, all classes related to multithreaded application are placed in the System.Threading namespace. It provides thread classes for creating threads, ThreadPool classes for managing thread pools, and so on, and provides a mechanism to solve practical problems such as thread execution scheduling, deadlock, and communication between threads. If you want to use multiple threads in your application, you must include this class. The thread class has several critical methods, described below:

Start (): Start thread
Sleep (int): Static method, pausing the number of milliseconds specified by the current thread
Abort (): This method is typically used to terminate a thread
Suspend (): This method does not terminate the unfinished thread, it simply suspends the thread and can be recovered later.
Resume (): Restores execution of threads suspended by the suspend () method
Let's start by creating a thread that simply provides a thread entry when you create a thread using the thread class. The thread entry lets the program know what to do with this thread, and in C #, the thread entry is provided through the ThreadStart Proxy (delegate), and you can interpret ThreadStart as a function pointer to the function that the thread is to execute. When the Thread.Start () method is invoked, the thread begins executing the function that the ThreadStart represents or points to.


Open your vs.net, create a new console Application (console application), and the following code will give you a complete control over a thread's endless fun!

//threadtest.cs

using System;
using System.Threading;

namespace ThreadTest
{
public class Alpha
{
public void Beta ()
{
while (true)
{
Console.WriteLine ("Alpha.beta is running into its own thread.");
}
}
};

public class Simple
{
public static int Main ()
{
Console.WriteLine ("Thread start/stop/join Sample");

Alpha Oalpha = new alpha ();
This creates a thread that executes the beta () method of the Alpha class
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
{
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 cannot be restarted.");
Console.ReadLine ();
}
return 0;
}
}
}

This program contains two classes Alpha and simple, and we use the initialization of the ThreadStart proxy (delegate) object to the Alpha.beta () method when creating the thread othread. When we create a thread othread invoke the Othread.start () method to start, the program actually runs the Alpha.beta () method:

alpha Oalpha = new Alpha ();
Thread othread = new Thread (new ThreadStart (Oalpha.beta));
Othread.start ();

And then in the while loop of the main () function, we use the static method Thread.Sleep () to let the main thread stop 1ms, and this time the CPU turns to execute thread othread. Then we tried to terminate the thread Othread with the Thread.Abort () method, noting the Othread.join (), Thread.Join () method, which causes the main thread to wait until the othread thread ends. You can give the Thread.Join () method a parameter of type int as the maximum time to wait. After that, we tried to restart the thread othread with the Thread.Start () method, but obviously the result of abort () method was an unrecoverable terminating thread, so the final program throws ThreadStateException exception.
What we should note here is that other threads are attached to the thread where the Main () function resides. The main () function is the entry point of a C # program, which can be called the main thread, and if all foreground threads are stopped, the main thread can terminate, and all background threads will terminate unconditionally. While all threads are serially executed on a microscopic level, you can think of them as being executed in parallel on a macro level.


Readers must have noticed the Thread.threadstate attribute, which represents the thread run-time state, has different values in different situations, so we can sometimes design the program flow by judging the value. The possible values of threadstate in various situations are as follows:

Aborted: Thread has stopped
AbortRequested: The thread's Thread.Abort () method has been invoked, but the thread has not stopped
Background: The thread executes in the background and is related to the property thread.isbackground
Running: Thread is running correctly
Stopped: Thread has been stopped
Stoprequested: Thread is being asked to stop
Suspended: The thread has been suspended (this state can be rerun by calling the Resume () method)
Suspendrequested: Thread is asking to be suspended, but not in time to respond
Unstarted: Thread.Start () Start thread running without calling
WaitSleepJoin: The thread is blocked because of a call to wait (), the Sleep (), or join ()

The background state indicates that the thread is running in the background, so what is the special place for the running thread in the background? In fact, there is only one difference between the background thread and the front thread, that is, the background thread does not interfere with the termination of the program. Once all foreground threads of a process terminate, the CLR (the common language runtime) terminates the process completely by invoking the abort () method of any surviving background process.

When the thread is competing for CPU time, the CPU is serviced at the priority level of the thread. In a C # application, a user can set 5 different priorities, from highest to lowest, highest,abovenormal,normal,belownormal,lowest, and if you do not specify a priority when creating a thread, Then the system defaults to Threadpriority.normal. Assign a priority to a thread
, we can use the following code:

//set Priority to minimum
mythread.priority=threadpriority.lowest;

By setting the priority of the thread, we can arrange some relatively important threads to execute first, such as the response to the user, and so on.

Now that we have an initial understanding of how to create and control a thread, we will delve into the more typical problems in threading implementations and explore their solutions.

Three. Synchronization and communication of threads-producers and consumers

In this case, two threads maintain a queue at the same time, if one thread adds elements to the queue, and another thread takes elements from the queue, we call the thread that adds the element as the producer, and the thread that takes the element is the consumer. The problem of producer and consumer seems simple, but it is a problem that must be solved in multi-threaded application, it involves the synchronization and communication problem between threads.

As I said before, each thread has its own resources, but the code area is shared, that is, each thread can perform the same function. But in a multithreaded environment, the potential problem is that several threads execute a function at the same time, causing data chaos and unpredictable results, so we have to avoid this happening. C # provides a keyword lock that defines a piece of code as a mutex segment (critical section), where the mutex only allows one thread to enter execution at a time while the other threads must wait. In C #, keyword LOCK is defined as follows:

lock (expression) statement_block
Expression represents the 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 a mutex code snippet inside a static method), use the class name generally. And Statement_block is the code for the mutex segment, which can only be executed by one thread at a time.


Here is a typical example of using the lock keyword, which I will explain in the comments about the use and purpose 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)
{
//throws an exception if the balance is less than 0
throw new Exception ("Negative Balance");
}
//The following code guarantees that before the current thread modifies balance value is complete
//no other thread will execute this code to modify the value of balance
//Therefore, the value of balance is unlikely to be less than 0
lock (This)
{
console.writeline ("Current Thread:" +thread.currentthread.name);
//If there is no protection for the lock keyword, it may be after the condition of the IF is executed
//Another thread has executed a Balance=balance-amount modified balance value
//and this modification is not visible to this thread, so it may cause the condition of if if is not established
//However, this thread continues to execute balance=balance-amount, so 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 < i++)
withdraw (R.next (-50, 100));
}
£}

internal class Test
{
static internal thread[] threads = new THREAD[10];
public static void Main ()
{
account acc = new account (0);
for (int i = 0; i < i++)
{
thread t = new Thread (The new ThreadStart (ACC). Dotransactions));
threads[i] = t;
}
for (int i = 0; i < i++)
threads[i]. Name=i.tostring ();
for (int i = 0; i < i++)
threads[i]. Start ();
console.readline ();
}
£}

While multithreading common an object, also appear and common code similar problem, this problem should not use the Lock keyword, Here you need to use a class monitor in System.Threading, which we can call a monitor, and monitor provides a solution that allows threads to share resources.

The Monitor class can lock an object, and a thread can manipulate the object only by getting the lock. The object lock mechanism guarantees that only one thread can access the object at a time when it can cause confusion. Monitor must be associated with a specific object, but because it is a static class, it cannot be used to define an object, and all of its methods are static and cannot be referenced using objects. The following code illustrates the case of locking an object using monitor:

.....
queue oqueue=new Queue ();
.....
monitor.enter (Oqueue);
......//now Oqueue object can only be manipulated by the current thread
monitor.exit (Oqueue);/release lock

As shown above, when a thread calls the Monitor.Enter () method to lock an object, the object is owned, and other threads want to access the object, only waiting for it to release the lock using the Monitor.Exit () method. To ensure that the thread eventually releases the lock, you can write the Monitor.Exit () method in the finally code block in the try-catch-finally structure. For any object that is locked by the monitor, there is some information about it in memory, one is the reference to the thread that now holds the lock, the other is a reserve column, the queue holds the thread that is ready to acquire the lock, and the third is a waiting queue, The queue holds a reference to the queue that is currently waiting for this object's state to change. When the thread that owns the object lock prepares to release the lock, it uses the Monitor.pulse () method to notify the first thread in the waiting queue, and the thread is transferred to the preliminary queue, and the thread in the preliminary queue can immediately acquire the object lock when the object lock is freed.

Below is an example of how to use the LOCK keyword and the monitor class to implement thread synchronization and communication, and is also a typical producer and consumer issue. In this routine, the producer and consumer threads are alternating, the producer writes a number, the consumer reads and displays it immediately, and I'll explain in the comments the gist of the program. The system namespaces used are as follows:

using System;
using System.Threading;





Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.