using System; using System.Threading.Tasks; namespace netcore2._1_ { class Program { static void Main(string[] args) { for (int i = 0; i < 20; i++) { //M(123); M4(456, i); Console.WriteLine("done:{0}", i); } // Console.WriteLine(456); Console.ReadKey(); } static async Task M(int x, int i) { await Task.Factory.StartNew(e => { Console.WriteLine(x); Task.Delay(1000); }, null); Console.WriteLine("async done:{0}", i); } static void M2(int x, int i) { Task.Factory.StartNew(e => { Console.WriteLine(x); Task.Delay(1000); }, null); Console.WriteLine("task done:{0}", i); } static void M3(int x) { Console.WriteLine(x); } /// <summary> /// 同步方法消费异步方法的接口,采用了状态共享穿越的闭包机制 /// async方法就是一个线程方法,只是这个线程方法可以合并其它的 /// await表达式的线程,产生相同可控的次序执行预期 这就是Task /// 链! /// 本方法通过void同步上下文去使用Task线程调用一个指定的async /// 方法,采用了,传递一个async匿名方法作用委托的方式进行,我们 /// 可以看到,这里并没有任何编译警告,无缝的对接了同步与异步,是 /// 最恰当的同步上下文消费异步async方法的封装! /// </summary> /// <param name="x"></param> /// <param name="i"></param> static void M4(int x, int i) { Task.Run(async () => { await M(x, i); }); } // 同步方法调用async的接口写法,也是用到一个委托和原来用的回调事件有相同之处 void M5(int x,int i) { Action<int,int> x2 = async (j,k) => await M(j,k); x2.Invoke(x,i); } } }
关于async/await对, async就是异步方法标注一个异步方法,只是被标记了,标记为async的方法内容里必须有至少一个await表达式,不然要报错,直接异步的还是await表达式下面的线程,这种线程可能是Task
搞的,也可以是Threead搞的(我看有例子是await 了Thread的方法)
await的 表达式,是个关键,它是个真正多线程产生的地方,不管是一个Task代表的线程还是一个Thread代表的线程,因为net中代表线程的就这两个类
https://blog.csdn.net/u011033906/article/details/62885545?utm_source=blogxgwz2
下面测试来看看语句块的线程id
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var t = new Test1(); t.Invoke(); Console.ReadKey(); } } class Test1 { string getCurrentThread => $"{Thread.CurrentThread.ManagedThreadId}"; public void Invoke() { for (int i = 0; i < 2; i++) { //Test11(); Test14(); } } async void Test11() { Console.WriteLine(getCurrentThread); Console.WriteLine(nameof(Test11)); Console.WriteLine(getCurrentThread); } async Task Test12() { Console.WriteLine($"{nameof(Test12)}-0:{getCurrentThread}"); Console.WriteLine($"sentence1"); await Task.Delay(1000); Console.WriteLine($"sentence2"); Console.WriteLine($"{nameof(Test12)}-1:{getCurrentThread}"); } async Task Test13(string content) { Console.WriteLine($"{nameof(Test13)}-0:{getCurrentThread}"); await Task.Delay(1000); Console.WriteLine(content); Console.WriteLine($"{nameof(Test13)}-1:{getCurrentThread}"); } async Task Test14() { Console.WriteLine($"{nameof(Test14)}-0:{getCurrentThread}"); Console.WriteLine($"sentence1"); await Test13($"Test14.Test13"); Console.WriteLine($"sentence2"); Console.WriteLine($"{nameof(Test14)}-1:{getCurrentThread}"); } } }
void Test19<T>(Func<T,Task> a, T t) {
Action<T> x = async x2 => await a(x2);
x.Invoke(t);
}
void Test20(int i) {
Test19<int>(Test17, i);
}
看看打印的线程id,似乎在不同的语句片断上有所不同
而改成非await的普通方法来测试似乎线程id并不会变见下图:
现在就已经可以证明了,被await分割的语句块从第一个await之后的代码块开始就已经与调用线程处于不同的线程了,并且如果你是一个async 1 : await * 的方法那么通常可能他会分一个额外的线程
如果循环多次进入异步就异步调用的次数是多次的话,那么,分派去处理的线程个数就会增加,最后可能会根本系统的情况来确定一个合理的线程数量组成一个线程池。
所以网上有说 async不创建线程的说法可能是不对的!
再来看看 两个测试结果
我们可以看到,当Task.Delay(0)的时候如果直接跑 似乎await并没有分出线程来了,可以说这是一个特殊的 Task对象,如果>0的那结果当然会是 非1(当前线程id)的,在使用Task.Run包裹一下Task.Delay(0) 时,我们看到 线程id不同了
再来看一个结果可以更清楚的说明问题所在:
这是循环执行2次的情况,可以发现,在同一个 current idx:i 下 sentence x 和对应的 sentence *x 的 线程id 号是一致的
得出的一些猜测:
1,一个await 表达式,并不等于分派一个线程,线程总数不会随着await表达式的增加而增加,
2, await和Task是一 一对应的,await表达式就是一个返回 Task的结果,所以有人说Task是占位符
3,语句片断按顺序执行
4,第一个语句片断是和调用线程相同的,后续的其它片断,可能处于一个或多个不同的线程之中
5,await之间的语句片断的线程与其前面的await表达式中Task 所属于的线程是一致的
6,async方法有污染扩散的倾向,但只要加上同步消费async接口方法的就可以解决这个问题
7,调用上下文的async消费接口方法,是使用一个对委托变量加上async标注来实现的,比如 Action<object> x= async x=> await Task_Expersion; x.Invoke(xx);
8,更简单的消费方式是 Task.Wait();方法,让异步变同步