Simple understanding of Task and its thread waiting and continuation, and brief description of Await/Async keywords

What is a Task?

Task is a class used to implement multithreading. In the previous version, Thread and ThreadPool already exist. Why should we propose the task class? This is because it is very troublesome to directly operate Thread and ThreadPool, pass parameters to the Thread, obtain the return value of the Thread and start and stop the Thread. Therefore, Microsoft engineers repackage the Thread. This is task. It can be said that task is based on Thread, Therefore, when multithreading, task is our first choice.

The thread of the task is the background thread.

Essentially, a task is an enhanced API for a thread pool.

Disadvantages of thread pool

1. It is inconvenient to get results from thread pool

2. Asynchronous processing is inconvenient

3. Continuous asynchronous operation is inconvenient

Purpose:

Scenario 1: three concurrent requests obtain data through three interfaces. We hope to obtain the data of all interfaces for the next business processing.

Scenario 2: three concurrent requests. As long as one of them is completed, an event result will be triggered.

Statement of Task

There are two ways to declare a Task:

a. Declare through new

 Task obj = new Task();

b. Through task Factory. Startnew

Task.Factory.StartNew(MyMethod);

The difference between the two declaration methods: the first declaration method must use obj to start the thread Start() and through task Factory. The startnew method is not used.

Task creation

1. Constructor creation

static void Main(string[] args)
{
    Task task = new Task(() =>
    {
        // True thread pool thread
        Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
    });
    task.Start();
    Console.ReadLine();
}

2.Task.Factory.StartNew create

Note: in the task factory mode, the task will be called and executed immediately.

static void Main(string[] args)
{
    Task.Factory.StartNew(() =>
    {
        // True thread pool thread
        Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
    });
    Console.ReadLine();
}

3. Do not use thread pool settings to create

static void Main(string[] args)
{
    Task task = new Task(() =>
    {
        // False do not use thread pool
        Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
    }, TaskCreationOptions.LongRunning);
    task.Start();
    Console.ReadLine();
}

Get task return value

Task<string> task = new Task<string>(() =>
{
    DoSomething();
    return "Here is the result of execution completion";
});
task.Start();
Console.WriteLine(task.Result);

Callback of task

task.ContinueWith

(1) Support one task connected to multiple ContinueWith;

(2) ContinueWith returns the next generation task, and the next generation can continue ContinueWith;

Task<string> task = new Task<string>(() =>
{
     Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
     return "boy,";
});
task.ContinueWith((t) =>
{
     Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
     Console.WriteLine(t.Result + "come on.");
});
task.Start();
Console.ReadLine();

Thread waiting and continuation of Task

Thread waiting and continuation under Task mainly include the following categories:

Wait

For a single Task instance, you can make a thread enter the wait.

WaitAny

The main thread will execute as long as one thread ends, otherwise it will get stuck.

WatiAll

Indicates that the main thread resumes after all the tasks in the task list have ended.

WhenAny

It means that after any task in the task list is completed, it will be triggered with ContinueWith.

WhenAll

It means that after all tasks in the task list are completed, the callback trigger is carried out in cooperation with ContinueWith.

Note: both WhenAny and WhenAll start a new thread for execution and do not jam the main thread.

TaskFactory

TaskFactory can start threads, and of course, it also corresponds to the waiting and continuation of threads.

ContinueWhenAny: equivalent to Task's WhenAny+ContinueWith

ContinueWhenAll: equivalent to Task's WhenAll+ContinueWith

Example code:

//1.ContinueWhenAny
Task.Factory.ContinueWhenAny(taskList.ToArray(),m=>{
	Console.WriteLine("")
})

//2.ContinueWhenAll
Task.Factory.ContinueWhenAll(taskList.ToArray(),m=>{
	Console.WriteLine("")
})

Complete code demonstration, create console:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        public static void Main(string[] args)
        {
            List<Task<string>> taskList = new List<Task<string>>();
            Task<string> t1 = new Task<string>(() =>
            {
                return GetName();
            });
            Task<string> t2 = new Task<string>(() =>
            {
                return GetMongo();
            });
            taskList.Add(t1);
            taskList.Add(t2);
            t1.Start();
            t2.Start();            
            Task.WhenAll(taskList.ToArray()).ContinueWith(x =>
            {
                Console.WriteLine("The task is completed");
                Console.WriteLine(x.Result[0]);
                Console.WriteLine(x.Result[1]);
            });
            Console.ReadLine();
        }
        public static string GetName()
        {
            Thread.Sleep(5000);
            return "MSSQL";
        }
        public static string GetMongo()
        {
            Thread.Sleep(10000);
            return "MongoDB";
        }
    }
}

The result is that the code runs for 10 seconds and outputs the corresponding contents in turn.

await and async keywords

When it comes to the return value of Task, we have to say await and async keywords. When the function is marked with async, the return value must be void, Task, Task < T >. When the return value is Task < T >, only T type needs to be returned inside the function, and the compiler will automatically wrap it into Task < T > type. The await keyword must be used within functions with async tags.

for instance:

static void Main(string[] args)
{
     Console.WriteLine("Call main thread");
     Task<string> s = Testasync();
     Console.WriteLine(s.Result);
     Console.ReadKey();
 
}
static async Task<string> Testasync()
{
      Console.WriteLine("function Task before" + Thread.CurrentThread.ManagedThreadId);
 
      Task<string> t= Task.Run<string>(() =>
      {
          Console.WriteLine("function Task" + Thread.CurrentThread.ManagedThreadId);
          Thread.Sleep();
          return "Thread test is me";
      });
      Console.WriteLine("function Task after" + Thread.CurrentThread.ManagedThreadId);
      var result = await t;
      return result;
}

async/await is a key word introduced by C#5.0. Its purpose is to make asynchronous programming more concise and to comply with the general trend of asynchronous programming.

function

1. async/await can be used to simply create asynchronous methods, which can prevent time-consuming operations from blocking the current thread.

2. async/await is used to build asynchronous methods, which is more concise.

grammar

① Method uses async as a modifier

② Method contains one or more await expressions that represent tasks that can be completed asynchronously

③ There must be the following three return types: void, Task and Task < T >, of which the return objects of the latter two identify the work to be completed in the future, and the calling method and asynchronous method can continue to be executed.

④ The parameters of an asynchronous method can be of any type, but they cannot be out and ref parameters

⑤ By convention, Async is generally used as the suffix of asynchronous methods.

⑥ In addition to methods, Lambda expressions and anonymous functions can also be used as asynchronous objects.

If await does not appear in async, the method becomes a synchronization method.

Control flow of asynchronous method

1 first, the part before the first await, which should be a small amount of code without waiting for a long time.

2await expression, which indicates the tasks that need to be executed asynchronously. There can be multiple awaits.

3 the rest of the code for the method that appears after the await expression.

give an example:

Example 1:

public static async Task DoSomethingAsync()
{
    Console.WriteLine("Asynchronous method synchronization part 1");
    long sum = 0;
    sum = await Task.Run(() =>
    {
        Console.WriteLine("Asynchronous execution part 1,thread  ID: {0}",Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(2000);
        long result = 0;
        for (var i = 0; i < 1000000000; i++)
        {
            result += i;
        }
        Console.WriteLine("End of asynchronous execution section");
        return result;
    });
    Console.WriteLine("Calculation results{0}", sum);
}

Example 2:

public static async void DoSomethingAsync()
 {
     Task t1 = new Task(()=> {

         Console.WriteLine("Asynchrony started,Thread:{0}", Thread.CurrentThread.ManagedThreadId);
         long result = 0;
         for (int i = 0; i < 1000000000; i++)
         {
             result += i;
         }
         Console.WriteLine("Asynchronous end, calculation result{0},thread {1}", result, Thread.CurrentThread.ManagedThreadId);

     });
     t1.Start();
     await t1;


 }

Comparison between async/await and ordinary asynchronous

Thinking: implement a program. After each task sleeps for 5 seconds, return to 100 and print 100

 public static void DoSomething()
 {
     Console.WriteLine("Asynchronous method start 1");
     var aaa= await Task.Run(() =>
     {
         Thread.Sleep(5000);
         return 100;
     });
 	Console.WriteLine("Calculation results{0}",  aaa.result);
 }

Call:

static void Main(string[] args)
 {
    DoSomething();
    DoSomething();
    DoSomething();
 }

Explanation: after the above code is run, it is executed synchronously. Although the Task will generate a thread to run asynchronously, AAA needs to be used because of the return value in the Task Result acquisition blocks the thread, so the running results are synchronized.

Convert the above code to asynchronous:

public  static void DoSomething111()
 {
     Console.WriteLine("Asynchronous method start 1");
     //The results are obtained through the first asynchronous method
     var aaa= Task.Run(() =>
     {
           Thread.Sleep(5000);
           return 100;
     });
     aaa.ContinueWith(x =>
     {
           Console.WriteLine(x.Result);
     });
 }

Explanation: due to Task Run () returns a Task object, so if you don't want to block the thread, you can call ContinueWith for callback processing, and get the return value of the Task in the callback. ContinueWith won't block the main thread.

 

Elegant writing async/await:

public async  static void DoSomething111()
 {
     Console.WriteLine("Asynchronous method start 1");
     //The results are obtained through the first asynchronous method
     var aaa= await Task.Run(() =>
     {
           Thread.Sleep(5000);
           return 100;
     });
     Console.WriteLine("The result is{0}", aaa);
 }

Explanation: task Run () executes a time-consuming task. The await flag indicates waiting for the task result and assigning the result to aaa. Yes, it's so elegant.

Tags: C#

Posted by jinky32 on Thu, 05 May 2022 21:20:23 +0300