C# Multithreading

First, the definition of thread


  Process (Process) is a basic concept in the Windows system, it contains the resources needed to run a program. Processes are relatively independent. One process cannot access the data of another process (unless distributed computing is used), and the failure of one process will not affect the operation of other processes. The Windows system uses processes to divide work into multiple an independent area. Process can be understood as the basic boundary of a program.


  A thread (Thread) is the basic execution unit in a process, and the first thread executed at the process entry is regarded as the main thread of the process. In .NET applications, the Main() method is used as the entry point. When this method is called, the system will automatically create a main thread. Threads are mainly composed of CPU registers, call stacks and thread local storage (Thread Local Storage, TLS). The CPU register mainly records the state of the currently executed thread, the call stack is mainly used to maintain the memory and data called by the thread, and the TLS is mainly used to store the state information of the thread.


  Multithreading, in a unit time (time slice) of a single CPU system, the CPU can only run a single thread, and the running order depends on the priority level of the thread. If the thread fails to complete execution within a unit of time, the system saves the thread's state information to the thread's local storage (TLS) so that execution can be resumed the next time it executes. And multithreading is just an illusion brought by the system, which switches between multiple threads in multiple units of time. Because the switching is frequent and the unit time is very short, multiple threads can be regarded as running at the same time.


  Appropriate use of multithreading can improve the performance of the system. For example, when the system requests large-capacity data, multithreading is used, and the data output work is handed over to asynchronous threads, so that the main thread maintains its stability to deal with other problems. But it should be noted that because the CPU needs to spend a lot of time on thread switching, excessive use of multi-threading will lead to performance degradation. (Dosage should be moderate)


2. Basic knowledge of threads


  2.1 System.Threading.Thread class


System.Threadubg.Thread is the basic class used to control threads. Thread can control the creation, suspension, stop and destruction of threads in the current medical program domain. It includes the following common public properties:


   Property name:


  CurrentContext: Gets the current context in which the thread is executing


  CurrentThread: Gets the currently running thread


  ExecutionContext: Gets an ExecutionContext object that contains information about the various contexts of the current thread.


  IsAlive: Gets a value that indicates the execution state of the current thread


  IsBackground: Gets or sets a value that indicates whether a thread is a background thread


  IsThreadPoolThread: Gets a value that indicates whether the thread belongs to a managed thread pool


  ManagedThreadId: Gets The unique identifier of the current managed thread


  Name: Gets or sets the thread's name


  Priority: Gets or sets a value that indicates the thread's scheduling priority


  ThreadState: Gets a value that contains the current thread's state


  2.1.1 The thread's identity The character


  ManagedThreadId is the unique identifier for identifying the thread. In most cases, the program identifies the thread through Thread.ManagedThreadId. And Name is a variable value. By default, Name is an empty value Null. Developers can set the name of the thread through the program, but this knowledge is an auxiliary function.


  2.1.2 The priority level


  of the thread. NET sets the Priority property for the thread to define the priority level of the thread execution. It contains 5 options, of which NORMAL is the default value. Unless the system has special requirements, the priority level of threads should not be set arbitrarily.


  Member name


  Lowerst: Thread can be scheduled after thread with any other priority


  BelowNormal: Thread can be scheduled after thread with Normal priority, before thread with Lowest priority


  Normal: Selected by default. Thread can be scheduled after thread with AboveNormal priority, before thread with BelowNormal priority


  AboveNormal: Thread can be scheduled after thread with Highest priority, before thread with Normal priority


  Highest: Thread can be scheduled


  2.1.3 The state of the thread before any other priority thread


  ThreadState can detect whether the thread is in the state of Unstarted, sleeping, running, etc. It can provide more specific information than the IsAlive property. As mentioned earlier, an application may include multiple contexts, and the current context of the thread can be obtained through CurrentContext. CurrentThread is the most commonly used attribute, which is used to obtain the currently running thread.


  2.1.4 Methods


  of System.Threading.Thread Thread includes multiple methods to control the creation, suspension, stop, and destruction of threads, which will be used frequently in future examples.


  Method name
  Abort(): Terminate this thread
  GetDomain(): Returns the current domain in which the current thread is running
  GetDomainId(): Returns the current domain Id in which the current thread is running
  Interrupt(): Interrupts the thread in the WaitSleepJoin thread state
  Join(): Overloaded. Block the calling thread until a thread terminates (let a thread finish running before starting to execute other threads)
  Resume(): Continue running the suspended thread
  Start(): Execute this thread
  Suspend(): Suspend the current thread thread, this has no effect if the current thread is in a suspended state

  Sleep(): Suspend the running thread for a period of time

        2.1.5 Development Example

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = Thread.CurrentThread;
            thread.Name="Main Thread ,Hello!";
            string threadMessage = string.Format("Thread ID:{0}\n"+"Current AppDomainId:{1}\n"+
                "Current ContextId:{2}\n"+"Thread Name:{3}\n"+
                "Thread State:{4}\n"+"Thread Priority:{5}\n",thread.ManagedThreadId,Thread.GetDomainID(),Thread.CurrentContext.ContextID,thread.Name,thread.ThreadState,thread.Priority);
            richTextBox1.Text = threadMessage;
        }
    }
}

operation result:


2.2 System.Threading namespace

  How many methods are provided in the System.Threading namespace to build multi-threaded applications, among which ThreadPool and Thread are the most commonly used in multi-threaded development. In .NET, a CLR thread pool is specially set to manage threads. Running, this CLR thread pool is verified to be managed by the ThreadPool class. Thread is the most direct way to manage threads, which will be described in detail in the following sections.

kind illustrate
AutoResetEvent Notifies waiting threads that an event has occurred. This class cannot be inherited.
ExecutionContext Manages the execution context of the current thread. This class cannot be inherited.
Interlocked Provides atomic operations for variables shared by multiple threads
Monitor         Provides a mechanism for synchronizing access to objects
Mutex A synchronization primitive that can also be used for inter-process synchronization
Thread Create and control a thread, set its priority and get its state
ThreadAbortException Exception thrown when a call to the Abort method is made. cannot inherit this class
ThreadPool Provides a pool of threads that can also be used to send work items, handle asynchronous I/O, wait on behalf of other threads, and handle timers
Timeout Contains constants that specify an infinite amount of time. cannot inherit this class
Timer Provides a mechanism for executing methods at specified intervals. cannot inherit this class
WaitHandle Encapsulates an OS-specific object waiting for exclusive access to a shared resource










2.3 Thread management

Creating a new thread through ThreadStart is the most direct method, but the threads created in this way are difficult to manage. If too many threads are created, the performance of the system will be degraded. In view of this, NET has specially set up a CLR thread pool for thread management. Using the CLR thread pool system can manage the use of threads more reasonably. All requested services can be run in the thread pool, and the thread will return to the thread pool when the operation ends. By setting, the maximum number of threads in the thread pool can be controlled. When the request exceeds the maximum thread value, the thread pool can be introduced here according to the basic knowledge of the priority of the operation. The following will introduce the development of multi-threading.

3. Implementing multi-threading in ThreadStart mode


3.1 Using ThreadStart delegate 


Here is an example to reflect the benefits of the following multi-threading. First, a method ShowMessage() is established in the Message class, which displays the ID of the currently running thread, and uses the Thread.Sleep(int) method to simulate part of the work. In main(), bind the ShowMessage() method of the Message object through the ThreadStart delegate, and then execute the asynchronous method through Thread.Start().

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
    public class Message
    {
        public void ShowMessage1()
        {
            string message = string.Format("Async1 threadId is :{0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(message);

            for (int n = 0; n < 12; n++)
            {
                Thread.Sleep(500);
                Console.WriteLine("The number1 is:" + n.ToString());
            }

        }
        public void ShowMessage2()
        {
            string message = string.Format("Async2 threadId is:{0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(message);
            for (int n = 0; n < 12; n++)
            {
                Thread.Sleep(500);
                Console.WriteLine("The number2 is:" + n.ToString());
            }

        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main threadId is:" + Thread.CurrentThread.ManagedThreadId);
            Message message = new Message();
            Thread thread1 = new Thread(new ThreadStart(message.ShowMessage1));//ThreadStart: execute method on thread
            Thread thread2 = new Thread(new ThreadStart(message.ShowMessage2));
            thread1.Start();//Start child thread
            thread2.Start();
            Console.WriteLine("Do something......!");
            Console.WriteLine("Main thread working is complete!");
            Console.ReadKey();//In order to stop the console, the main thread runs to this sentence and stops, waiting for the user to input any key to end the main thread
        }
    }
}


Pay attention to the running result, after calling the Thread.Start() method, the system runs Message.ShowMessage1() and Message.ShowMessage2() asynchronously, while the operation of the main thread continues, in Message.ShowMessage1() and Message. Before ShowMessage2() is completed, the main thread has completed all output operations and entered the state of waiting for user input (the last sentence of the main program Console.ReadKey()), if you press any key, you will find that the window will not stay, because the main thread has ended early due to your key press.

The existence of multi-threading makes the program at least appear not to be executed sequentially, as if multiple programs are running at the same time.


3.2 Using the ParameterizedThreadStart delegate

  The ParameterizedThreadStart delegate is very similar to the ThreadStart delegate, but the ParameterizedThreadStart delegate is for methods with parameters. Note that the parameter of the corresponding method of ParameterizeThreadStart (the method that is placed in the thread and ready to run) is object. This parameter can be a value object or a custom object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{

    public class Person
    {
        public string Name
        {
            get;
            set;

        }
        public int Age
        {
            get;
            set;
        }
    }
    public class Message
    {
        public void ShowMessage(object person)
        {
            if (person != null)
            {
                Person _person = (Person)person;
                string message = string.Format("\n{0}'s age is {1}!\nAsync threadId is:{2}", _person.Name,
                    _person.Age, Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine(message);
            }
            for (int n = 0; n < 12; n++)
            {
                Thread.Sleep(500);
                Console.WriteLine("The number1 is:" + n.ToString());
            }

        }

    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main threadId is:" + Thread.CurrentThread.ManagedThreadId);
            Message message = new Message();
            Thread thread = new Thread(new ParameterizedThreadStart(message.ShowMessage));//ThreadStart: execute method on thread
            Person person = new Person();
            person.Name = "Jack";
            person.Age = 21;
            thread.Start(person);//Start child thread

            Console.WriteLine("Do something......!");
            Console.WriteLine("Main thread working is complete!");
            Console.ReadKey();
        }
    }
}



3.3 Foreground thread and background thread


In the above two examples, the last program of the main thread is Console.ReadKey(), so that the window can stop after the sub-thread ends. If there is no such program in the two examples, it can be found that the system is still Will wait for the asynchronous thread to complete before ending. This is because the thread started with Thread.Start() is the foreground thread by default, and the system must wait for all foreground threads to finish running before the application domain is automatically unloaded.


In the second section, the thread Thread has an attribute IsBacground. By setting this attribute to true, the thread can be set as a background thread! In this case the application domain will be unloaded when the main thread completes, without waiting for the asynchronous thread to run.


3.4 Suspend a thread


To wait for other background threads to finish before ending the main thread, use the Thread.Sleep() method

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
    public class Message
    {
        public void ShowMessage()
        {
            string message = string.Format("\nAsync threadId is :{0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(message);
            for (int n = 0; n < 10; n++)
            {
                Thread.Sleep(100);
                Console.WriteLine("The number is:" + n.ToString());
            }
        }
    }
    class Program
    {
        static void Main(string[] args)//Main thread
        {
            Console.WriteLine("Main threadId is:" + Thread.CurrentThread.ManagedThreadId);
            Message message = new Message();
            Thread thread = new Thread(new ThreadStart(message.ShowMessage));
            thread.IsBackground = true;
            thread.Start();

            Console.WriteLine("Do something ..........!");
            Console.WriteLine("Main thread working is complete!");
            Console.WriteLine("Main thread sleep!");
            Thread.Sleep(5000);
        }

    }
}

3.5 Suspend and Resume (use with caution)


Thread.Suspend() and Thread.Resume() are old methods that existed in Framework1.0, they can suspend and resume threads respectively. However, these two methods have been explicitly excluded in Framework2.0. This is because once a thread occupies an existing resource and uses Suspend() to keep the thread in a suspended state for a long time, it will cause deadlock when other threads call these resources! So these two methods should be avoided unless necessary.


3.6 Terminate the thread


If you want to terminate the running thread, you can use the Abort() side to resume the execution of the thread. After catching the exception, you can call Thread.ResetAbort() in catch(ThreadAbortException ex){...} to cancel the termination.


Using Thread.Join() can ensure that the application domain waits for the asynchronous thread to end before terminating the operation.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
   
    class Program
    {
        static void Main(string[] args)//Main thread
        {
            Console.WriteLine("Main threadId is:" + Thread.CurrentThread.ManagedThreadId);
            Thread thread = new Thread(new ThreadStart(AsyncThread));
            thread.IsBackground = true;
            thread.Start();
            thread.Join();//Know that the thread thread ends and then run other threads
            Console.WriteLine("The child thread has finally finished running, it's my main thread's turn, my thread's unique identification number: " + Thread.CurrentThread.ManagedThreadId);
            Console.ReadKey();
        }
        static void AsyncThread()
        {
            try
            {
                string message = string.Format("\nAsync threaddId is:{0}", Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine(message);
                for (int n = 0; n < 10; n++)
                {
                    if (n >= 4)
                    {
                        // when n = 4, terminate the thread
                        Thread.CurrentThread.Abort(n);

                    }
                    Thread.Sleep(500);
                    Console.WriteLine("the number is:" + n.ToString());
                }

            }
            catch (ThreadAbortException ex)
            {
                // output the value of n when the thread is terminated
                if (ex.ExceptionState != null)
                    Console.WriteLine(string.Format("Thread abort when the number is:{0}",ex.ExceptionState.ToString()));
                //Cancel the termination and continue executing the thread
                Thread.ResetAbort();
                Console.WriteLine("Thread ResetAbort!");

            }
            // thread ends
            Console.WriteLine("Thread Close!");
        }
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325480313&siteId=291194637