C#语法——await与async的正确打开方式 C#基础系列——异步编程初探:async和await
C#语法——await与async的正确打开方式
C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。
为什么呢?我觉得大家的await与async的打开方式不正确。
正确的打开方式
1、await 只能在标记了async的函数内使用。
2、await 等待的函数必须标记async。
有没有感觉这是个循环?没错,这就是个循环。这也就是为什么大家不怎么用他们的原因。这个循环很讨厌,那么怎么破除这个循环呢?
【很简单,await等待的是线程,不是函数。】
不理解吗?没关系,接着看下去。
下面从头来讲解,首先看这么一组对比
1
2
3
4
5
6
7
8
|
public static int NoAsyncTest() { return 1; } public static async Task< int > AsyncTest() { return 1; } |
async Task<int>等于int
这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
当然不是,那什么时候会让 await AsyncTest()有意义呢?
我们接着往下看,修改AsyncTest如下。然后,此时再调用await AsyncTest(),你会神奇的发现,依然没有卵用。。。
Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); await AsyncTest(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); } public static async Task< int > AsyncTest() { Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }); return 1; } |
别着急,我们稍作调整,在线程后面增加.GetAwaiter().GetResult()。这句话是干什么用的呢?是用来获取线程返回值的。
这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
运行一下,我们将看下面的结果。
1
2
3
4
5
6
7
8
9
|
public static async Task< int > AsyncTest() { Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); return 1; } |
但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
那么怎么才能让他起作用呢?
首先,我们定义一个普通函数,他的返回值是一个Task,然后我们得到Task后,运行它,再用await等待这个Task。
于是我们就得到这样的结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); var waitTask = AsyncTestRun(); waitTask.Start(); int i = await waitTask; Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); } public static Task< int > AsyncTestRun() { Task< int > t = new Task< int >(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); return 100; }); return t; } |
如图,这样写await AsyncTest();就起作用了。
所以,还是那句话,await等待的是线程,不是函数。
但在图里,我们发现很奇怪的一点,结束Excute也是线程3,而不是线程1。也就是说,Await会对线程进行优化。
下面看下两组代码的对比,让我们就更清楚的了解下Await。
第一组,使用await等待线程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); await SingleAwait(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); } public static async Task SingleAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now); await Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }); await Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now); return ; } |
第二组,使用等待线程结果,等待线程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); await SingleNoAwait(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); } public static async Task SingleNoAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now); Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now); return ; } |
可以明确的看到,第二组,线程重新回到了主线程1中,而第一组,已经被优化到了线程4中。
await是一种很便捷的语法,他的确会让代码简洁一些,但他主动优化线程的功能,如果不了解就使用,可能会导致一些奇怪的BUG发生。
这也是官方为什么只提供了await调用服务的例子,因为,在程序内调用,await还是要了解后,再使用,才安全。
----------------------------------------------------------------------------------------------------
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的推荐,非常感谢!
C#基础系列——异步编程初探:async和await
前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法。确实,没有异步的多线程是单调的、乏味的,async和await是出现在C#5.0之后,它的出现给了异步并行变成带来了很大的方便。异步编程涉及到的东西还是比较多,本篇还是先介绍下async和await的原理及简单实现。
C#基础系列目录:
- C#基础系列——Linq to Xml读写xml
- C#基础系列——扩展方法的使用
- C#基础系列——序列化效率比拼
- C#基础系列——反射笔记
- C#基础系列——Attribute特性使用
- C#基础系列——小话泛型
- C#基础系列——多线程的常见用法详解
- C#基础系列——委托和设计模式(一)
- C#基础系列——委托和设计模式(二)
- C#基础系列——再也不用担心面试官问我“事件”了
之前的那篇 C#基础系列——多线程的常见用法详解 就讲到了多线程new Thread()的方式对于有返回值类型的委托是没有解决方案的,如果需要返回值,必须要依靠异步的方式。了解异步之前,我们先来看看Thread对象的升级版本Task对象:
1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
static void Main(string[] args) { Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法 Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine(strRes.Result);//得到方法的返回值 Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.ReadLine(); } static string GetReturnResult() { Thread.Sleep(2000); return "我是返回值"; }
先来看结果:
从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
static void Main(string[] args) { Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var strRes = Task.Run<string>(() => { return GetReturnResult(); }); Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine(strRes.Result); Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.ReadLine(); } static string GetReturnResult() { Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); return "我是返回值"; }
结果:
由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
Task对象的用法如下:
//用法一 Task task1 = new Task(new Action(MyAction)); //用法二 Task task2 = new Task(delegate { MyAction(); }); //用法三 Task task3 = new Task(() => MyAction()); Task task4 = new Task(() => { MyAction(); }); task1.Start(); task2.Start(); task3.Start(); task4.Start();
由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。
2、初识 async & await。
static void Main(string[] args) { Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); TestAsync(); Console.ReadLine(); } static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var name = GetReturnResult(); Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); } static async Task<string> GetReturnResult() { Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId); return await Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); return "我是返回值"; }); }
结果:
我们来看看程序的执行过程:
由上面的结果可以得到如下结论:
(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。
(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。
(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。
(4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:
异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。
3、除了await关键字,Task对象还有另外一种方式等待执行结果。
static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var name = GetReturnResult(); Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); }
这样可以得到相同的结果。
name.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion { public bool IsCompleted { get; } public TResult GetResult(); public void OnCompleted(Action continuation); public void UnsafeOnCompleted(Action continuation); }
IsCompleted:获取一个值,该值指示异步任务是否已完成。
GetResult():得到执行的结果。这个方法和await关键字效果相同。
OnCompleted():传入一个委托,在任务执行完成之后执行。
UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。
由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace async异步_Task分派任务_await等待
{
public class Test
{
public async Task TestAsync()
{
await GetAsync();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
public async Task GetAsync()
{
await Task.Delay(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
}
class Program
{
//使用 async 关键字定义的异步方法简称为“异步方法”。
//如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
//async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
//不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
//一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
//异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
// 方法签名包含 async 修饰符。
//按照约定,异步方法的名称以“Async”后缀结尾。
//返回类型为下列类型之一:
//如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
//如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
//void:如果要编写异步事件处理程序。
//包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
//方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
static void Main(string[] args)
{
Test t = new Test();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
t.TestAsync();
Console.ReadLine();
}
}
}
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace await与async的正确打开方式 { class Program { static void Main(string[] args) { //await 只能在标记了async的函数内使用。 //await 等待的函数必须标记async。 //【很简单,await等待的是线程,不是函数。】 Console.WriteLine(Thread.CurrentThread.GetHashCode()); NoAsyncTest(); AsyncTest(); //await AsyncTest(); Console.WriteLine(); Excute(); Console.ReadKey(); } public static int NoAsyncTest() { return 1; } public static async Task<int> AsyncTest()//警告 7 此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。 {// async Task<int>等于int //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢? //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。 //当然不是,那什么时候会让 await AsyncTest()有意义呢? Task.Run(() => {//线程变化了 创建了新线程 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); return 1; } public static async void Excute() {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); await AsyncTest(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); } }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。 }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace 正确打开方式 { class Program { public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。 waitTask.Start(); //int i = 0; int i = await waitTask; Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); Console.ReadKey(); } public static Task<int> AsyncTestRun() { Task<int> t = new Task<int>(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); return 100; }); return t; } public static async Task<int> AsyncTest() { Task.Run(() => //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。 {//创建了新线程 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); return 1; } static void Main(string[] args) //错误 1 “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); //AsyncTest(); //正常调用不会创线程 //AsyncTest(); //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程 五行 //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。 //Excute(); Console.WriteLine("--"); Console.WriteLine("--"); Excute2(); //Excute3(); Console.ReadKey(); } public static async void Excute2() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now); await SingleAwait(); //等待线程结束 然后执行后面的 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now); } public static async Task SingleAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now); await Task.Run(() => //await 等待线程完成 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(3000); }); await Task.Run(() => //Await会对线程进行优化。 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now); return; } public static async void Excute3() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now); await SingleNoAwait(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now); } public static async Task SingleNoAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now); Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。 //await 不等待 Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now); return; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Http; using System.Threading; namespace msdntask { class Program { //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。 static void Main(string[] args) {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行 Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。 //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。 //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。 Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine(strRes.Result); Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕 这段太绝对 跟主线程执行到哪 子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚: //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。 //应该是 子线程值 阻塞主线程 因为主线程要得到返回值,必须要等方法执行完成。 Task.Run 会开子线程一起执行 Console.WriteLine("---------------------"); Console.WriteLine("---------------------"); Console.WriteLine("---------------------"); //用法一 Task task1 = new Task(new Action(MyAction)); //用法二 Task task2 = new Task(delegate { MyAction(); }); //用法三 Task task3 = new Task(() => MyAction()); Task task4 = new Task(() => { MyAction(); }); //Task对象的构造函数传入的是一个委托 //task1.Start(); //task2.Start(); //task3.Start(); //task4.Start(); Console.WriteLine("-------00-------"); Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); TestAsync(); Console.ReadLine(); Console.ReadLine(); } static void MyAction() { Console.WriteLine("MyAction " + Thread.CurrentThread.ManagedThreadId); } static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); //var name = GetReturnResult2(); // 不用 await 不等待 主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法) // 加上 await 当异步方法 等待 var name = await GetReturnResult2(); // 等待 异步方法里面 线程执行完 返回 string //加上 await 返回 string 不加Task<TResult> // 不加 普通方法 Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); } static async Task<string> GetReturnResult2() { Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId); //return await Task.Run(() => // await只限于 当前异步方法等待 //return ""; return await Task.Run(() => // await只限于 当前异步方法等待 //错误 4 由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>” // 这里 Task.Run 会直接创线程执行的 异步操作的Result 才是 值 返回值 这里返回Task<string> { Thread.Sleep(5000); Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); return "我是返回值"; }); } static string GetReturnResult() { Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); //Thread.Sleep(2000); Thread.Sleep(10000); return "我是返回值"; } //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。 // await 会等待 线程执行完成 // Task.Run 和主线程一起执行 但是如果有返回值 主线程会卡住 等计算完 返回值 } }
task 操作任务会开启子线程 执行任务
如果主线程 要用到子线程里的返回值
则等待 子线程操作完成 task对象里的一个属性 提供返回值
await 等待 异步方法里执行完毕
await 必须调用 有异步关键字的方法
await 会等待里面异步方法执行完成
再执行主代码
异步关键字 修饰为异步方法
如果没有await 调用 就跟普通方法一样
await 必须存在 异步修饰方法中
异步修饰方法不一定 要有 await
我的手机 19:50:38
task 操作任务
会开启子线程 执行任务
和主线程一起执行
如果主线程 要用到子线程里的返回值
则等待 子线程操作完成 task对象里的一个属性 提供返回值
await 等待 异步方法里执行完毕
await 必须调用 有异步关键字的方法
await 会等待里面异步方法执行完成
再执行后续代码
异步关键字 修饰为异步方法
如果没有await 调用 就跟普通方法一样
await 必须存在 异步修饰方法中
异步修饰方法不一定 要有 await
我的手机 19:56:05
await 虽然 会按顺序执行完代码
但里面的线程是否会改变
回到主函数 线程是否会改变?
我的手机 20:12:00
看到
进异步方法的时候 没有改变 还是用的主线程
进task.run的时候 task会创子线程
而且await 会等待
await等待的是线程回归。之所以改变,是因为把await的子线程当成主线程了,因为这跟await的功能一致 等待
等待执行完 所以回来主线程改变
我的手机 20:13:55
所以await 不会改变线程
我的手机 20:14:03
task 才会
我的手机 20:18:57
task 创子线程
我的手机 20:19:46
没有用 await task.run task.run 改变 (创子线程 一起执行) await 等待 只有task.run 不会改变主流程线程
就不改变线程
我的手机 20:20:32
那就是 不等待
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace 正确打开方式 { class Program { public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。 waitTask.Start(); //int i = 0; int i = await waitTask; Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); Console.ReadKey(); } public static Task<int> AsyncTestRun() { Task<int> t = new Task<int>(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); return 100; }); return t; } public static async Task<int> AsyncTest() { Task.Run(() => //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。 {//创建了新线程 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); return 1; } static void Main(string[] args) //错误 1 “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); //AsyncTest(); //正常调用不会创线程 //AsyncTest(); //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程 五行 //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。 //Excute(); Console.WriteLine("--"); Console.WriteLine("--"); //Excute2(); Excute3(); Console.ReadKey(); } public static async void Excute2() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now); await SingleAwait(); //等待线程结束 然后执行后面的 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now); } public static async Task SingleAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now); await Task.Run(() => //await 等待线程完成 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(3000); }); await Task.Run(() => //Await会对线程进行优化。 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now); return; } public static async void Excute3() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now); await SingleNoAwait(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now); } public static async Task SingleNoAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now); Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。 //await 不等待 Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now); return; } } }
Excute3();
public static async void Excute3()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
await SingleNoAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
}
await 虽然等待 只等待一个 要是里面有Task.Run 还是会创子线程一起执行
public static async Task SingleNoAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
//await 不等待
//.GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。 把这句拿掉 会创子线程一起 执行 // await 虽然等待 只等待一个 要是里面有Task.Run 还是会创子线程一起执行 加上后 就等待 子线程 执行完 主线程才继续执行
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
});//.GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
return;
}
等10秒
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Http; using System.Threading; namespace msdntask { class Program { //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。 static void Main(string[] args) {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。 //GetAwaiter().GetResult(); 主线程等待子线程10S 主线程继续执行 不加主线程和子线程一起执行 //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。 //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。 Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine(strRes); Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕 这段太绝对 跟主线程执行到哪 子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚: //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。 //应该是 子线程值 阻塞主线程 因为主线程要得到返回值,必须要等方法执行完成。 Task.Run 会开子线程一起执行 Console.WriteLine("---------------------"); Console.WriteLine("---------------------"); Console.WriteLine("---------------------"); //用法一 Task task1 = new Task(new Action(MyAction)); //用法二 Task task2 = new Task(delegate { MyAction(); }); //用法三 Task task3 = new Task(() => MyAction()); Task task4 = new Task(() => { MyAction(); }); //Task对象的构造函数传入的是一个委托 //task1.Start(); //task2.Start(); //task3.Start(); //task4.Start(); Console.WriteLine("-------00-------"); Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); TestAsync(); Console.ReadLine(); Console.ReadLine(); } static void MyAction() { Console.WriteLine("MyAction " + Thread.CurrentThread.ManagedThreadId); } static async Task TestAsync() { Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); //var name = GetReturnResult2(); // 不用 await 不等待 主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法) // 加上 await 当异步方法 等待 var name = await GetReturnResult2(); // 等待 异步方法里面 线程执行完 返回 string //加上 await 返回 string 不加Task<TResult> // 不加 普通方法 Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss")); Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); } static async Task<string> GetReturnResult2() { Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId); //return await Task.Run(() => // await只限于 当前异步方法等待 //return ""; return await Task.Run(() => // await只限于 当前异步方法等待 //错误 4 由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>” // 这里 Task.Run 会直接创线程执行的 异步操作的Result 才是 值 返回值 这里返回Task<string> { Thread.Sleep(5000); Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId); return "我是返回值"; }); } static string GetReturnResult() { Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId + " shijian" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")); //Thread.Sleep(2000); Thread.Sleep(10000); return "我是返回值"; } //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。 // await 会等待 线程执行完成 // Task.Run 和主线程一起执行 但是如果有返回值 主线程会卡住 等计算完 返回值 } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace 正确打开方式 { class Program { public static async void Excute() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。 waitTask.Start(); //int i = 0; int i = await waitTask; Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); Console.ReadKey(); } public static Task<int> AsyncTestRun() { Task<int> t = new Task<int>(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); return 100; }); return t; } public static async Task<int> AsyncTest() { Task.Run(() => //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。 {//创建了新线程 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); return 1; } static void Main(string[] args) //错误 1 “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); //AsyncTest(); //正常调用不会创线程 //AsyncTest(); //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程 五行 //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。 //Excute(); Console.WriteLine("--"); Console.WriteLine("--"); //Excute2(); Excute3(); Console.ReadKey(); } public static async void Excute2() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now); await SingleAwait(); //等待线程结束 然后执行后面的 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now); } public static async Task SingleAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now); await Task.Run(() => //await 等待线程完成 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(3000); }); await Task.Run(() => //Await会对线程进行优化。 { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now); return; } public static async void Excute3() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now); await SingleNoAwait(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now); } public static async Task SingleNoAwait() { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now); Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。 把这句拿掉 会创子线程一起 执行 await 虽然等待 只等待一个 要是里面有Task.Run 还是会创子线程一起执行 加上后 就等待 子线程 执行完 主线程继续执行 //await 不等待 Task.Run(() => { Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now); return; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace async异步_Task分派任务_await等待 { public class Test { public async Task TestAsync() { await GetAsync(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); } public async Task GetAsync() { await Task.Delay(1); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); } } class Program { //使用 async 关键字定义的异步方法简称为“异步方法”。 //如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。 //async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。 //不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。 //一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。 //异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。 // 方法签名包含 async 修饰符。 //按照约定,异步方法的名称以“Async”后缀结尾。 //返回类型为下列类型之一: //如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。 //如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。 //void:如果要编写异步事件处理程序。 //包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。 //方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。 static void Main(string[] args) { Test t = new Test(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); t.TestAsync(); Console.ReadLine(); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace await与async的正确打开方式 { class Program { static void Main(string[] args) { //await 只能在标记了async的函数内使用。 //await 等待的函数必须标记async。 //【很简单,await等待的是线程,不是函数。】 Console.WriteLine(Thread.CurrentThread.GetHashCode()); NoAsyncTest(); AsyncTest(); //await AsyncTest(); Console.WriteLine(); Excute(); Console.ReadKey(); } public static int NoAsyncTest() { return 1; } public static async Task<int> AsyncTest()//警告 7 此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。 {// async Task<int>等于int //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢? //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。 //当然不是,那什么时候会让 await AsyncTest()有意义呢? Task.Run(() => {//线程变化了 创建了新线程 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now); Thread.Sleep(1000); }).GetAwaiter().GetResult(); return 1; } public static async void Excute() {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。 Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now); await AsyncTest(); Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now); } }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。 }