C # basic series - multithreading and asynchrony

Keywords: C#

1, Process and thread

1. Process: a running application is regarded as a process in the operating system, which contains the resources required by an application. A process can contain one or more threads. Processes are independent of each other. One process cannot access the data of another process. The communication between different applications and data requests can be regarded as the communication between different processes.

2. Thread: the basic execution unit in a process. It is the basic unit of CPU time allocated by the operating system. The first thread running at the process entrance is regarded as the main thread of the process.

3. Multithreading: the CPU runs too fast and the processing speed of other hardware (IO) can't keep up, so the operating system manages by time slice. The macro view is multithreading concurrency. Because the hardware is multi-core and multi CPU, one CPU can only run one thread at a time, and multiple CPUs can run multiple threads at the same time.

2, Synchronous and asynchronous methods

Synchronous and asynchronous are used to modify methods. When a method is called, the caller needs to wait for the method to complete execution and return to continue execution. This method is a synchronous method. When a method is called, it returns immediately (get a thread to execute the internal business of the method). The caller does not have to wait for the method to complete execution. The method is an asynchronous method.

3, Thread usage

The. NET FrameWork uses the Thread evolution process, which is created and managed by using the Thread class. Because threads create multiple threads, the system overhead is large, so the pooling technology is used; Define the ThreadPool class to manage Thread resources in the way of Thread pool, so as to reduce the number of threads created and the cost of managing threads, because ThreadPool cannot control the execution order of threads and cannot obtain the notice of cancellation / exception / completion in the Thread pool; Based on the above shortcomings, NET4.0 proposes that tasks are encapsulated by ThreadPool, which has the advantages of Thread pool and solves its disadvantages that are not easy to control. Create a Task, execute a Task, essentially start a Thread, and better control the execution process of threads in the form of tasks Net 5.0 can write asynchronous programming like synchronous code through the keyword async/await, which is based on Task.

 1 /// <summary>
 2 /// Thread
 3 /// </summary>
 4 private static void StartThread()
 5 {
 6     // Create a thread and perform operations
 7     Thread thread = new Thread((obj) => {
 8         Console.WriteLine("Start a thread operation");
 9         Console.WriteLine(obj);
10     });
11     thread.Start();
12     Console.WriteLine("Main thread");
13 }
14 
15 /// <summary>
16 /// ThreadPool
17 /// </summary>
18 private static void StartThreadPool()
19 {
20     // Create a thread pool, get threads from the thread pool, and perform operations
21     // The execution order of threads in the thread pool cannot be controlled, and the cancellation of threads in the thread pool cannot be obtained/abnormal/Notification of completion
22     for (var i = 0; i < 10; i++)
23     {
24         ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
25         {
26             Console.WriteLine($"The first{obj}task");
27         }), i);
28     }
29     Console.WriteLine("Main thread");
30 }
31 
32 /// <summary>
33 /// Execute the main thread first, indicating that the created thread will not be blocked (without return value) task)
34 /// </summary>
35 private static void StartTask()
36 {
37     // adopt Task Instantiate a thread to perform operations
38     Task task = new Task(() =>
39     {
40         Thread.Sleep(100);
41         Console.WriteLine($"hello, task Thread ID by{Thread.CurrentThread.ManagedThreadId}");
42     });
43     task.Start();
44 
45     //2.Task.Factory.StartNew(Action action)Create and start a Task
46     Task task1 = Task.Factory.StartNew(() =>
47     {
48         Thread.Sleep(100);
49         Console.WriteLine($"hello, task1 Thread ID by{Thread.CurrentThread.ManagedThreadId}");
50     });
51 
52     //3.Task.Run(Action action)Put the task in the process pool queue, return and start a Task
53     Task task2 = Task.Run(() =>
54     {
55         Thread.Sleep(100);
56         Console.WriteLine($"hello, task2 Thread ID by{Thread.CurrentThread.ManagedThreadId}");
57     });
58     Console.WriteLine("Main thread");
59 }
60 
61 /// <summary>
62 /// With return value Task
63 /// </summary>
64 private static void StartTaskReturn()
65 {
66     Task<string> task = new Task<string>(() =>
67     {
68         Thread.Sleep(100);
69         return $"hello, task Thread ID by{Thread.CurrentThread.ManagedThreadId}";
70     });
71     task.Start();
72 
73     Task<string> task1 = Task.Factory.StartNew<string>(() =>
74     {
75         Thread.Sleep(100);
76         return $"hello, task1 Thread ID by{Thread.CurrentThread.ManagedThreadId}";
77     });
78 
79     Task<string> task2 = Task.Run<string>(() =>
80     {
81         Thread.Sleep(100);
82         return $"hello, task2 Thread ID by{Thread.CurrentThread.ManagedThreadId}";
83     });
84 
85     // be careful task.Resut The thread is blocked when the result is obtained, that is, if task If the execution is not completed, it will wait task Get after execution Result,Then execute the following code
86     Console.WriteLine(task.Result);
87     Console.WriteLine(task2.Result);
88     Console.WriteLine(task1.Result);
89     Console.WriteLine("Main thread");
90 }

  4, Thread blocking

Thread blocking is to block the execution of the main thread. After executing other threads, the main thread is executed by using the Join method in the thread. If there are multiple threads, multiple threads call Join and finally execute the main thread. The thread blocking method should call the Join method in turn. If the thread execution is completed or one execution is completed, the blocking cannot be removed immediately. In the Task, Wait is used (single execution completed) is equivalent to Join, WaitAll and WaitAny to block the main thread.

/// <summary>
/// Thread block
/// </summary>
private static void ThreadTest()
{
    Thread th1 = new Thread(() =>
    {
        Thread.Sleep(500);
        Console.WriteLine("Thread 1 execution completed!");
    });
    th1.Start();

    Thread th2 = new Thread(() =>
    {
        Thread.Sleep(100);
        Console.WriteLine("Thread 2 execution completed!");
    });
    th2.Start();

    //Block main thread
    th2.Join();
    th1.Join();
    Console.WriteLine("Main thread execution completed!");
}

/// <summary>
/// Wait/WaitAny/WaitAll
/// </summary>
private static void TasksTest()
{
    Task task1 = new Task(() => {
        Thread.Sleep(500);
        Console.WriteLine("Thread 1 execution completed!");
    });
    task1.Start();
    Task task2 = new Task(() => {
        Thread.Sleep(1000);
        Console.WriteLine("Thread 2 execution completed!");
    });
    task2.Start();

    //Wait(Single)
    //task1.Wait();
    //task2.Wait();

    // WaitAll(whole)
    // task1 And task2 All execution is completed in the main thread of execution
    // Task.WaitAll(new Task[] { task1, task2 });
    // task1 And task2 When an execution is completed, the main thread is executed

    // WaitAny((there is one)
    Task.WaitAny(new Task[] { task1, task2 });
    Console.WriteLine("Main thread execution completed!");
}

5, Thread continuation

If you want the thread to execute subsequent operations after execution, use the continuation of Task WhenAny (there is an execution completion), WhenAll (all threads have completed execution), ContinueWith (single thread has completed execution). This method is a non blocking method, and the main thread will complete execution first.

/// <summary>
/// Task Continuation operation of(WhenAny/WhenAll/ContinueWith)
/// </summary>
private static void TasksTest1()
{
    Task task1 = new Task(() => {
        Thread.Sleep(500);
        Console.WriteLine("Thread 1 execution completed!");
    });
    task1.Start();
    Task task2 = new Task(() => {
        Thread.Sleep(1000);
        Console.WriteLine("Thread 2 execution completed!");
    });
    task2.Start();

    /*
    Task.WhenAll(new Task[] { task1, task2 }).ContinueWith((t) =>
    {
        Console.WriteLine($"id={t.Id}");
        Thread.Sleep(100);
        Console.WriteLine("task1 Both task 2 and task 2 are completed when executing this operation ");
    });
    */

    Task.WhenAny(new Task[] { task1,task2}).ContinueWith((t) =>
    {
        Console.WriteLine($"id={t.Id}");
        Thread.Sleep(100);
        Console.WriteLine("task1 And task2 There is an execution completion in performing this operation");
    });
}

6, Thread cancellation

Thread cancellation is to exit the execution during thread execution. In the thread, the thread polls the variables by using the identifier (variable). If the status changes, the execution content will be exited. In the Task, cancel the Task execution through the CancellationTokenSource class.

/// <summary>
/// thread Task cancellation
/// </summary>
public static void ThreadCancel()
{
    // Define cancellation ID
    bool isStop = false;

    int index = 0;
    Thread th1 = new Thread(() =>
    {
        while (!isStop)
        {
            Thread.Sleep(1000);
            Console.WriteLine($"The first{++index}Second execution, thread running...");
        }
    });
    th1.Start();

    Thread.Sleep(3000);
    isStop = true;
}

/// <summary>
/// Task cancellation
/// </summary>
private static void TaskCancel()
{
    CancellationTokenSource source = new CancellationTokenSource();
    int index = 0;
    //Open a task Perform tasks
    Task task1 = new Task(() =>
    {
        while (!source.IsCancellationRequested)
        {
            Thread.Sleep(1000);
            Console.WriteLine($"The first{++index}Second execution, thread running...");
        }
    });
    task1.Start();

    //Cancel the task in five seconds
    Thread.Sleep(5000);
    //source.Cancel()Method requests to cancel the task, IsCancellationRequested Will become true
    source.Cancel();
}

/// <summary>
/// Task The task is cancelled and executed callback
/// </summary>
private static void TaskCancelCallBack()
{
    // Create a task cancellation token object, pass in the task, monitor and cancel the task
    CancellationTokenSource source = new CancellationTokenSource();

    // Register task cancellation event callback
    source.Token.Register(() =>
    {
        Console.WriteLine("Actions performed after task cancellation");
    });

    int index = 0;

    //Open a task Perform tasks
    Task task1 = new Task(() =>
    {
        while (!source.IsCancellationRequested)
        {
            Thread.Sleep(1000);
            Console.WriteLine($"The first{++index}Second execution, thread running...");
        }
    });

    task1.Start();
    //Delay cancellation, the effect is equivalent to Thread.Sleep(5000);source.Cancel();
    source.CancelAfter(5000);
}
/// <summary>
/// use async and await Read file contents asynchronously
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private async static Task<string> GetTextAsync(string fileName)
{
    using (FileStream fs = new FileStream(fileName, FileMode.Open))
    {
        var bytes = new byte[fs.Length];
        Console.WriteLine("Start reading file contents");
        //ReadAync Method reads the content asynchronously without blocking the thread
        int len = await fs.ReadAsync(bytes, 0, bytes.Length);
        string result = Encoding.UTF8.GetString(bytes);
        
        return result;
    }
}

7, Summary

Asynchronous method is to execute other tasks without blocking the main thread by creating threads. It can be used to handle time-consuming operations such as local IO and network io. Multithreading is one of the ways to realize asynchronous and one of the purposes of asynchronous multithreading. In the way of realizing asynchronous, you can also hand over tasks to other applications by using Multithreading can not only realize asynchronous programming, but also be used for large amount of data analysis to improve efficiency.

Posted by ketanco on Sun, 07 Nov 2021 18:02:05 -0800