Net4.6 Task Async function is 5 times times slower than a synchronous function The pit experience https://www.cnblogs.com/shuxiaolong/p/DotNet_Task_BUG.html
Asynchronous Task Brief Introduction
This headline is a bit of a grandstanding, everyone don't mind (do not rule out personal technical ability)--Next: I will use a small demo to explain the idea of this article.
. Net 4.0 has a Task function--Asynchronous programming model
. Net 4.6 Adds several particularly useful methods to the Task, and introduces the await async syntax sugar
Of course, this is a very good technology, but I have my own thread queue package, there is no hurry to use this thing.
In the end, the task async function
Recently, there are projects that need to use the asynchronous socket framework for Dotnetty.
This framework is the Java netty--of the Microsoft team and can be interfaced with the existing Java Netty.
I'm not going to introduce you to the Netty.
Dotnetty based on. NET 4.3 (actually requires at least. NET 4.5)--Yes, you're right. NET 4.3
All right, follow me down the hole and learn some of the usage specifications for asynchronous task functions.
Let's look at one of the simplest demos to see the asynchronous power of a Task
Copy Code
1 static void Main (string[] args)
2 {
3//simulation of a business requirement: There are 200 strings to handle
4 List List = new List ();
5 for (int i = 0; i <; i++) list. ADD ("AAAA" + i);
6
7
8
9 DateTime TIME0 = DateTime.Now;
11//Handle these strings with multiple tasks
List listtask = new list ();
foreach (string item in list)
{
Task task = Task.run () +
+ {
Handle (item ); Executes a method that handles more than 200 strings of
18});
Listtask.add (Task);
Task.waitall (Listtask.toarray ());//waits for 200 strings to be processed to complete
for DateTime time1 = DateTime.Now;
Console.WriteLine
("200 string processing completed, synchronous execution takes 200 seconds, actual task execution time:" + (TIME1-TIME0). TotalSeconds + "seconds");
+
+
+
public static void Handle (String item)
35 {
Thread.Sleep (1000);//processing time 1 seconds
Console.WriteLine ("Processing" + Item);
Block}
Copy code
The processing logic of the business is not so simple.
In fact, we have AAAA0 ~ AAAA199 a total of 200 strings
However, the actual processing of the string requires a Strhandler class.
And, Strhandler has a property Type, if strhandler.type==1, then this strhandler can only handle AAAA1 AAAA11 .... AAAA191 these strings ending in 1
Then: 200 strings require a minimum of 10 strhandler to handle. Theory: 200 strings, create 200 strhandler to deal with it? However: the Strhandler constructor has some initialization operations, which is time consuming and takes 5 seconds.
Let's take a look at how slow the new 200 strhandler will be. If you use a sync function, that is (1+5) *200 = 1200 seconds
Copy Code
1 class Program
2 {
3 static void Main (string[] args)
4 {
5//simulation of a business requirement: There are 200 strings to handle
6 List List = new List ();
7 for (int i = 0; i <; i++) list. ADD ("AAAA" + i);
8
9
10
One DateTime TIME0 = DateTime.Now;
12
13//processing these strings with multiple tasks
List Listtask = new List ();
foreach (string item in list)
16 {
+ Task task = Task.run (() = =
18 {
Handle (item); Executes a method that handles these 200 + strings
20});
Listtask.add (Task);
22}
Task.waitall (Listtask.toarray ()); Wait 200 strings to finish processing
24
DateTime time1 = DateTime.Now;
26
27
28
Console.WriteLine ("200 string processing completed, synchronous execution takes 200 seconds, actual task execution time:" + (TIME1-TIME0). TotalSeconds + "seconds");
30
31
32
33}
34
35
$ public static void Handle (String item)
37 {
38//The last digit of the string is the Type value of the Strhandler
A. int temp = Convert.ToInt32 (item). Substring (item. LENGTH-1));
40
Strhandler handler = new Strhandler (temp);
Handler. Handle (item);
43}
44
45}
46
47//String processing class
-Public class Strhandler
49 {
public strhandler (int type)
51 {
type = type;
Thread.Sleep (5000); Creating a Strhandler takes 5 seconds
54}
55
$ public int Type {get; set;}
57
58
Handle public void (string item)
60 {
Thread.Sleep (1000); function itself call takes 1 seconds
Console.WriteLine ("Processor {0} handles string {1}", Type, item);
63}
64}
Copy Code
Multiplexing Strhandler reduces overhead
Because the Strhandler needs to create a TCP communication channel, it opens up multiple network ports that will consume unnecessary. 200 strings with a minimum of 10 strhandler--so: We'll just create 10 strhandler.
We modify the static Handle () function as follows
Copy Code
1//public static void Handle (String item)
2//{
3//Int temp = Convert.ToInt32 (item. Substring (item. LENGTH-1)); The last digit of the string is the Type value of the Strhandler
4
5//Strhandler handler = new Strhandler (temp);
6//handler. Handle (item);
7//}
8
9 public static void Handle (String item)
10 {
One Strhandler handler = GetHandler (item);
Handler. Handle (item);
13}
14
The private static Hashtable hash = hashtable.synchronized (new Hashtable ());
16
17//different strings have different Strhandler
//strhandler is an expensive resource, initializing the Strhandler takes 5 seconds, so the gethandler needs to be cached
private static Strhandler GetHandler (String item)
20 {
//lock (hash)//plus lock does not affect the final theory of this article
22 {
temp = Convert.ToInt32 (item. Substring (item. LENGTH-1));
24
Strhandler handler = hash[temp] as Strhandler;
if (handler! = null) return handler;
27
28//If there is no cache, create Strhandler
Handler = new Strhandler (temp);
Hash[temp] = handler;
return handler;
32}
33}
Copy Code
We used Hashtable to cache the Strhanlder class--and look at the performance
Strhandler Initializing Dotnetty Communication
The above several evolution, the performance gradually improved a lot. Business requirements: Strhandler needs to communicate with Dotnetty, and before calling Strhandler's Handle (string), it is necessary to have dotnetty complete initialization.
Look at the improved Strhandler
Copy Code
1//String processing class
2 public class Strhandler
3 {
4 public strhandler (int type)
5 {
6 type = type;
7 Dotnetty = new Dotnetty ();
8
9//thread.sleep (5000);
Ten Dotnetty.start (); The time consuming 5 seconds is actually the dotnetty of the timing//call is a hypothetical synchronization method
11}
12
public int Type {get; set;}
Public Dotnetty Dotnetty {get; set;}//Add Dotnetty class (This is a heavyweight object, not new)
15
16//function itself call takes 1 seconds
+ public void Handle (string item)
18 {
if (! dotnetty.active)
The throw new Exception (string. Format ("{0} Dotnetty is not active, cannot execute Handle", Type));
21st
Thread.Sleep (1000);
Console.WriteLine ("Processor {0} handles string {1}", Type, item);
24}
25}
26 Let's take a look at the definition of dotnetty (simulation definition)
27
28//The following code simulates the Dotnetty framework-this framework provides only the asynchronous task Method Startasync ();
29//So: Startasync () definition cannot be modified
public class Dotnetty
31 {
The//dotnetty only provides asynchronous functions
Public async Task Startasync ()
34 {
Await Task.run (() =
36 {
The PNs//dotnetty is a well-known communication framework that normally initializes in 1 seconds.
38//But in special cases, initialization takes 5 seconds (e.g. the destination IP port does not exist at all)
Thread.Sleep (5000);
40});
41
Active = true; Dotnetty initialization complete Also, the Dotnetty is set to the active state
43}
44
45//Assuming a synchronous Start () method is provided to the Dotnetty
46//actually: Dotnetty does not have this synchronization method
public void Start ()
48 {
Thread.Sleep (5000);
Active = true; Dotnetty initialization complete Also, the Dotnetty is set to the active state
51}
52
$ public bool Active {get; set;}
54}
Copy Code
Dotnetty does not provide a start () method, we assume that a synchronous method is added to start ()
The test is to assume the performance of a synchronous function Start ().
Dotnetty only provides asynchronous task methods Startasync ()
As we have said above, Dotnetty only provides the Startasync () method.
The Start () we just simulated does not exist.
At this time, experienced small partners must be able to point out:
Without the synchronization function, we can encapsulate the asynchronous task function as a synchronous function!
That's right, we can extend a Start () method to Dotnetty,
Copy Code
1 public static class Extend
2 {
3 public static void Start (this dotnetty dotnetty)
4 {
5 Task task = Dotnetty.startasync ();
6 task. Wait (); Let the asynchronous task wait to complete, this is not a synchronous method?
7}
8}
Copy Code
In order to confirm the conjecture, I have also deliberately written a test code.
Copy Code
1 static void Main (string[] args)
2 {
3 Dotnetty dotnetty = new Dotnetty ();
4 Dotnetty.start ();
5 Console.WriteLine ("Dotnetty.active:" + dotnetty.active);
4?
Copy Code
After adding the extension method, the program compiles the
Official operation
This paper summarizes
This article, through a simple demo, demonstrates how to use task Async programming to kill a case.
Finally came the following conclusions:
The pseudo-synchronization function of a task async function through the Wait () wrapper is unreliable.
The Task.waitall () function is the largest pit (this is the newly added function of. Net 4.6?). )
Dotnetty does not provide a synchronous function of Start (), only providing Startasync () is not nice.
Recommendation: All underlying libraries, you can have a task function, but keep the synchronization function.
Never be too careful to prove, just be bold
This paragraph can be used as a joke--don't be serious.
Copy Code
Ps.
Before we wrote the function, we prepared the synchronous function, the callback function
After. Net 4.5, an asynchronous function model was introduced
In the above example, we see an asynchronous task method that can be used as a callback function and as a synchronous function
--you might think that the async task method is so powerful
However, warning suggestions:
Countless internet users (including the great God) asynchronous task to trample the pit experience, including their own this pit,
Have come to the conclusion that the asynchronous task can only go to the black one way
That is, you use asynchronous tasks in one place, and other references go up, and you have to change to async.
You retort: I can encapsulate an asynchronous task into a synchronous function, ah, task. Wait (); Return task. Result;
This is the beginning of the pit: you might see a thread soar to 900, but CPU utilization is 0%.
-and finally, you'll go back to the very beginning: Asynchronous task can only go to the black
--900 threads, CPU 0%, how to check the wrong is the problem: every function seems to be no problem (is really no problem), strung together after the operation of suspended animation
--You think it's a deadlock? Maybe a few minutes later, 900 threads are all running again.
-There is no deadlock, it seems to be a Task kernel implementation of a resource allocation BUG (in order not to lead to deadlock, so it soared 900 threads of resource allocation scheme)
Task Async functions are powerful, but please do not misuse
Synchronous functions are encapsulated into asynchronous functions without any problems
But the task async function encapsulates the synchronous function (the pseudo-synchronous function)--This will be the beginning of your nightmare.
There is an intuitive conjecture (possibly incorrect):
A () is a kernel asynchronous task function that opens up 10 tasks (all released when done),
B () is the underlying asynchronous task function, for some reason B () calls a pseudo-synchronous function of a (), B opens up 10 tasks
C () is an upper-level call function, he calls the pseudo-sync function of B (), C opens up 10 tasks to work
--Finally, once suspended animation occurs, the number of threadsmay be equal to 10 = 1000, or 2000
--The number of threads in the event of suspended animation, and the nested hierarchy of pseudo-synchronization functions for asynchronous tasks
--imagine that a () references a more kernel library of NLog, which day NLog the author to change his code to an asynchronous task, a () encapsulates a NLog pseudo-sync function (which preserves the consistency of previous code calls) for minimal code churn, assuming that NLog opens up 5 tasks
510*10 = 5,000 threads estimated to be out of the box "personal guessing, don't mind."
The rotation and the vicissitudes of the sun and moon--can provide task asynchronous functions, but keep synchronous functions (especially the underlying framework) as much as possible
Copy Code
Written in the last words
There are programmers who can understand synchronization functions, barely understand the callback function, and do not understand the async function at all-it is a complete imitation of writing await async.
As a bottom-level architect, if your bottom-up is an async function on top of the big one-will you let the developers who use your framework encounter bugs like today?
Synchronous functions are like pro-force, callback functions like software outsourcing, asynchronous functions like the management of a large company-the strength of many people at the same time, if the management is not good, it may happen a small number of people to do, the result is also the embarrassment of wrangling ~
Net4.6 Task Async function is 5 times times slower than the sync function. Pit Experience