Method and steps of asynchronous programming in. NET Core

Time:2019-9-9

Recently, there has been some inspiration for asynchronous and multithreaded programming, so I decided to write down my understanding.

Think: Why use asynchronous programming?

Let’s first look at the logic executed in the program before synchronous and asynchronous methods:

1. Synchronization method

static void Main(string[] args)
{
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: Start");
  // Call the synchronization method
  SyncTestMethod();
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: End");
  Console.ReadKey();
}

/// <summary>
/// Synchronization method
/// </summary>
static void SyncTestMethod()
{
  for (int i = 0; i < 10; i++)
  {
    var str = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ms")}:SyncTestMethod{i}";
    Console.WriteLine(str);
    Thread.Sleep(10);
  }
}

Console Printing:

2019-03-26 14:44:05 445:
2019-03-26 14:44:05 445:SyncTestMethod0
2019-03-26 14:44:05 445:SyncTestMethod1
2019-03-26 14:44:05 445:SyncTestMethod2
2019-03-26 14:44:05 445:SyncTestMethod3
2019-03-26 14:44:05 445:SyncTestMethod4
2019-03-26 14:44:05 445:SyncTestMethod5
2019-03-26 14:44:05 445:SyncTestMethod6
2019-03-26 14:44:05 445:SyncTestMethod7
2019-03-26 14:44:05 445:SyncTestMethod8
2019-03-26 14:44:05 445:SyncTestMethod9
2019-03-26 14:44:05 445:end

When the main thread invokes the synchronization method, it executes the synchronization method directly in the main thread. At this time, if there are other methods behind the SyncTestMethod method, it needs to wait for the SyncTestMethod to execute.

 

2. Asynchronous method

static void Main(string[] args)
{
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: Start");
  // Calling an asynchronous method
  AsyncTestMethod();
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: End");
  Console.ReadKey();
}


/// <summary>
/// Asynchronous method
/// </summary>
/// <returns></returns>
static async Task AsyncTestMethod() {
  await Task.Run(() => {
    for (int i = 0; i < 10; i++)
    {
      Console.WriteLine($"AsyncTestMethod");
      Thread.Sleep(10);
    }
  });
}

Console Printing:

2019-03-26 14:52:37 5237:
2019-03-26 14:52:37 5237:end
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod
2019-03-26 14:52:37 5237:AsyncTestMethod

When the main thread calls the asynchronous method, it will create a new sub-thread to execute the asynchronous method. After calling the AsyncTestMethod method, it will directly execute the method after the AsyncTestMethod. At this time, the main thread will not wait for the asynchronous method to execute, because the main thread can not know what the asynchronous method will be. When the execution is completed, it is impossible to get the return of the asynchronous method directly in the main thread. If you need to execute other methods in the main thread after the execution of the asynchronous method, you need to use Wait () to wait for the execution of the asynchronous sub-thread to complete.

3. Waiting Asynchronous Method

static void Main(string[] args)
{
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: Start");
  // Calling an asynchronous method
  AsyncTestMethod();
  // Waiting for asynchronous method execution to complete
  m1.Wait();
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: End");
  Console.ReadKey();
}


/// <summary>
/// Asynchronous method
/// </summary>
/// <returns></returns>
static async Task AsyncTestMethod() {
  await Task.Run(() => {
    for (int i = 0; i < 10; i++)
    {
      Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ms")}:AsyncTestMethod");
      Thread.Sleep(10);
    }
  });
}

Console Printing:

2019-03-26 14:55:51 5551:
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:AsyncTestMethod
2019-03-26 14:55:51 5551:End

When the main thread calls the asynchronous method, it will create a new sub-thread to execute the asynchronous method, and after calling the AsyncTestMethod method, it will execute the Wait () waiting for the AsyncTestMethod method. At this time, the main thread will wait for the asynchronous method to execute, and will not execute the subsequent method, and complete the execution of the AsyncTestMethod. Then, when the wait is over, you can get the return value of the asynchronous method AsyncTestMethod, and then continue to execute the method in the main thread.

4. Synchronized thread and asynchronous thread associated execution

If the following methods are available:

static int Method1()
{
  Thread.Sleep(200);
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I calculated a value that consumed 200 ms";
  return 1;
}
static int Method200ms()
{
  Thread.Sleep(200);
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I did a 200 ms thing");
  return 200;
}
static int Method500ms(int index)
{
  Thread.Sleep(500);
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I did a 500 ms thing");
  return ++index;
}
static int Method1000ms()
{
  Thread.Sleep(1000);
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I did a 1000ms thing");
  return 1000;
}

Method500ms () requires the return value of Method1 () as a parameter, if all methods are executed synchronously in the final calculation of the sum of a, b, c, d:

static void Main(string[] args)
{
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: Start");
  var a = Method1();
  var b = Method200ms();
  var c = Method500ms(a);
  var d = Method1000ms();
  var result = a+b+c+d;
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: The final result {result}";
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: End");
  Console.ReadKey();
}

Console Printing:

2019-03-26 15:10:06 106:
2019-03-26 15:10:06106: I calculated a value costing 200 ms
2019-03-26 15:10:06106: I did something that cost 200 ms.
2019-03-26 15:10:07 107: I did something that cost 500 ms.
2019-03-26 15:10:08 108: I did a 1000ms thing.
2019-03-26 15:10:08 108: The final result 1203
2019-03-26 15:10:08 108:end

When executing synchronously, we need to wait for all methods to complete one by one. The time spent is obviously the sum of the time spent by all methods.

For the above four methods, if they are executed asynchronously, the running time of the program will be greatly saved. The modification method is as follows:

static async Task<int> AsyncMethod1()
{
  await Task.Run(()=> {
    Thread.Sleep(200);
    Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I calculated a value that consumed 200 ms";
  });
  return 1;
}
static async Task<int> AsyncMethod200ms()
{
  await Task.Run(() => {
    Thread.Sleep(200);
    Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I did a 200 ms thing");
  });
  return 200;
}
static async Task<int> AsyncMethod500ms(int index)
{
  await Task.Run(() => {
    Thread.Sleep(500);
    Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I did a 500 ms thing");
  });
  return ++index;
}
static async Task<int> AsyncMethod1000ms()
{
  await Task.Run(() => {
    Thread.Sleep(1000);
    Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: I did a 1000ms thing");
  });
  return 1000;
}

Call methods asynchronously:

static void Main(string[] args)
{
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: Start");
  var m1 = AsyncMethod1();
  var m2 = AsyncMethod200ms();
  var m4 = AsyncMethod1000ms();
  m1.Wait();
  var m3 = AsyncMethod500ms(m1.Result);
  m2.Wait();
  m3.Wait();
  m4.Wait();
  var result = m1.Result + m2.Result + m3.Result + m4.Result;
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: The final result {result}";
  Console. WriteLine ($"{DateTime. Now. ToString ("yyyyy-MM-dd HH: mm: SS ms")}: End");
  Console.ReadKey();
}

Console Printing:

2019-03-26 14:11:54:1154:
2019-03-26 14:11:54:1154: I calculated a cost of 200 ms
2019-03-26 14:11:54:1154: I did something that cost 200 ms.
2019-03-26 14:11:55:1155: I did something that cost 500 ms.
2019-03-26 14:11:55:1155: I did a 1000ms thing.
2019-03-26 14:11:55:1155: The final result 1203
2019-03-26 14:11:55:1155:

becauseAsyncMethod500ms()Dependence onAsyncMethod1()The return result is taken as a parameter, so we can run directly in an asynchronous manner first.AsyncMethod1()AsyncMethod200ms()AsyncMethod1000ms()Three methods, at this time, all three methods will establish asynchronous sub-threads to execute, but laterAsyncMethod500ms()If you want to execute, you must haveAsyncMethod1()The return value of theAsyncMethod1()Waiting,200msLater,AsyncMethod1()Execution is completed.m1.Wait()Waiting for the end to continueAsyncMethod500ms()And introducedAsyncMethod1()The return value ofm1.ResultFinally, because we need to accumulate the return values of the four methods, we must ensure that the other three methods are completed before doing so, so we need to separate them.AsyncMethod500ms()AsyncMethod200ms()AsyncMethod1000ms()Wait, because all methods are executed asynchronously at the moment, so the execution time of the program will be the execution time of the method with the longest execution time.(AsyncMethod1000ms()implement1000msThe longest execution time, the execution time of the program1000ms )。

After reading the above content, we can be sure that in some cases, asynchronous programming can greatly improve the efficiency of our program operation, but everyone is advocating the use of asynchronous programming not only because of the reasons above the software, but also because of the hardware.

Some time ago, we published the original program of an office computer on a two-way E5 DELL blade machine. It was found that the performance of the DELL blade machine was worse than that of the previous office computer. At first, we suspected that the DELL blade machine was a virtual machine problem, and maybe there was no set-up in some places. After a series of server performance tests, whether CPU processing speed, disk IO or network bandwidth, the DELL blade machine is far superior to our previous office computer, but the efficiency of some interface in our program is not as good as the previous office computer!!!

Until one day, as my understanding of. NET Core asynchronous programming deepened, I finally understood why. )

Let’s take a look at the comparison between the Intel CPU we use in our daily development and the CPU we use in our servers.

Develop computer CPU:Intel Core i5 + 8500 processor

  • Processor base frequency: 3GHz
  • Maximum Rui Frequency: 4.1GHz
  • Number of Kernels: Six Cores
  • Number of threads: six threads

Server CPU:Intel to strong D-2177NT processor

  • Processor base frequency: 1.90 GHz
  • Maximum Rui Frequency: 3.00 GHz
  • Number of kernels: 14
  • Thread count: 28

From the above comparison, we can see that the difference between the two is obvious.i5The processor’s basic frequency and maximum core frequency are higher than those used by the server.xeonProcessors, but far less than the number of cores and threadsxeonIf all our programs use synchronization programming, take WebApi as an example, the method invoked in each request is only in a certain core/thread of the CPU. In other words, the frequency of CPU single core directly affects the efficiency of synchronization method, and almost all our previous programs use synchronization. Method, on the office computeri5Processor and server usagexeonThe difference in processor single-core frequencies is clearly the direct cause of previous performance problems.

In view of the characteristics of the server CPU (low single-core frequency, large number of kernels/threads), the use of asynchronous/multithreading in the program is undoubted for the performance of the program.

Be careful:

Although asynchronous programming often improves program efficiency, it does not mean that all methods need to be changed to asynchronous execution in order to use asynchrony. If the cost of synchronous execution is even lower than the cost of creating an asynchronous thread, there is no need to use asynchronous mode here. As for the trade-off between advantages and disadvantages, it may take some experience to get hold of it.

This is my current understanding of asynchronous/multithreaded programming in. NET Core. I hope it will be helpful to everyone’s learning, and I hope you will support developpaer more.

Recommended Today

Write a package for parsing abnormal JSON strings – fbbk-json

F [beep] [beep] K JSON Parse a package of abnormal JSON strings. install $ npm install fbbk-json Use This package has only one function: var fJSON = require(“fbbk-json”); fJSON.parse(A_JSON_STRING); however This package supports the following JSON strings (that is, what we usually declare in javascript, without quotation marks). Raise a chestnut. ‘{foo”:”bar”,”baz”: true} < – […]