第一个知识点:调用async异步方法的线程,遇到await,马上回去继续执行后面的操作,
而async异步方法里面,则是执行完任务,再继续执行await关键字后面的内容,
相当于await关键字后面内容,是在回调中完成,可在第三篇代码中体会这一点,对比task.ContinueWith()
呈现出一定程度的有序性,这就是大家讨论的同步编程方式写异步
本质是语法糖,编译器进行编译,基于状态机模式实现
这里await必须有async,不能单独存在,并且他是修饰Task
加了async和await支行,原本返回void,可以返回Task,原本返回T类型,返回Task
如下,一般不返回void如上,使用时就不能加await
第二个知识点:通过await访问任务,而不是Result
Result 属性为阻止属性。 如果你在其任务完成之前尝试访问它,当前处于活动状态的线程将被阻止,直到任务完成且值为可用。 在大多数情况下,应通过使用 await 访问此值,而不是直接访问属性。
上一示例通过检索 Result 属性的值来阻止主线程,从而使 ShowTodaysInfo 方法可在应用程序结束之前完成执行。
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/async-return-types#task-return-type
一句话,异步方法用Result非常容易陷入死锁,
Result先阻塞主线程,等待异步方法执行完,但异步方法体执行完,却return回不去主线程(它被阻塞)
Result先阻塞主线程,等待异步方法执行完,但异步方法体执行完,却return回不去主线程(它被阻塞)
Result先阻塞主线程,等待异步方法执行完,但异步方法体执行完,却return回不去主线程(它被阻塞)
await关键字,
只能在通过 async 关键字修改的方法、lambda 表达式或匿名方法中使用 await 运算符。 在异步方法中,不能在同步函数的主体、lock 语句块内以及不安全的上下文中使用 await 运算符。
await 运算符的操作数通常是以下其中一个 .NET 类型:Task、Task
如果表达式 t 的类型为 Task
async 和 await 关键字在 C# 5 和更高版本中都可用。
https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/await
官方说的很清楚,await只能用在以上类型的操作数,
备注:为了看得子线程输出内容,一定要在Main函数中,添加Console.Read();
//异步多线程调用方式,本质是一种语法糖,并不是什么新的调用方式
new AsyncClass().Show();
//这个Read非常关键,相当于一直在等待其他子线程去完成
Console.Read();
return;
并且窗体程序选中控制台输出,
体会执行顺序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncDemo
{
class AsyncDemo
{
//一般多线程开启
public void Show() {
Console.WriteLine("This is Main Start " + Thread.CurrentThread.ManagedThreadId);//1
//第一:
//一般多线程方法:挂起开启子线程内容,其他内容正常执行,即1,2,3,4,5,6
//this.NoReturn();
//第二: //加上异步之后,子线程第一步和主线程第二步,并发执行,最大变化是第三步
this.NoReturnAsync();
//第三:加上回调
//this.NoReturnWithContinue();
//this.ReturnTaskAsync();
Console.WriteLine("This is Main End " + Thread.CurrentThread.ManagedThreadId);//4 主线程第二步
}
public void NoReturn()
{
Console.WriteLine("This is NoReturn Method Start " + Thread.CurrentThread.ManagedThreadId);//2
var task = Task.Run(() =>
{
Console.WriteLine("This is NoReturn task Start " + Thread.CurrentThread.ManagedThreadId);//5 子线程第一步
Thread.Sleep(2000);
Console.WriteLine("This is NoReturn task End " + Thread.CurrentThread.ManagedThreadId);//6
});
//task.Wait();
Console.WriteLine("This is NoReturn Method End " + Thread.CurrentThread.ManagedThreadId);//3
}
public void NoReturnWithContinue()
{
Console.WriteLine("This is NoReturn Method Start " + Thread.CurrentThread.ManagedThreadId);//2
var task = Task.Run(() =>
{
Console.WriteLine("This is NoReturn task Start " + Thread.CurrentThread.ManagedThreadId);//5 子线程第一步
Thread.Sleep(2000);
Console.WriteLine("This is NoReturn task End " + Thread.CurrentThread.ManagedThreadId);//6
});
//task.Wait();
task.ContinueWith(t => {
Console.WriteLine("This is NoReturn Method End " + Thread.CurrentThread.ManagedThreadId);//3
});
}
//async可以单独写,此时和没写差不多
public async void NoReturnAsync()
{
Console.WriteLine("This is NoReturn Method Start "+Thread.CurrentThread.ManagedThreadId);//2
var task = Task.Run(()=>{
Console.WriteLine("This is NoReturn task Start " + Thread.CurrentThread.ManagedThreadId);//3
Thread.Sleep(2000);
Console.WriteLine("This is NoReturn task End " + Thread.CurrentThread.ManagedThreadId);
});
await task;
Console.WriteLine("This is NoReturn Method End " + Thread.CurrentThread.ManagedThreadId);
var task2 = Task.Run(() =>
{
Console.WriteLine("This is NoReturn2 task Start " + Thread.CurrentThread.ManagedThreadId);//3
Thread.Sleep(2000);
Console.WriteLine("This is NoReturn2 task End " + Thread.CurrentThread.ManagedThreadId);
});
await task2;
Console.WriteLine("This is NoReturn2 Method End " + Thread.CurrentThread.ManagedThreadId);
}
//这里await必须有async,不能单独存在,并且他是修饰Task
//加了async和await支行,原本返回void,可以返回Task,原本返回T类型,返回Task
//如下,一般不返回void如上,使用时就不能加await
//
public async Task ReturnTaskAsync()
{
Console.WriteLine("This is ReturnTaskAsync Method Start " + Thread.CurrentThread.ManagedThreadId);
var task = Task.Run(() =>
{
Console.WriteLine("This is ReturnTaskAsync task Start " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.WriteLine("This is ReturnTaskAsync task End " + Thread.CurrentThread.ManagedThreadId);
});
await task;
//最大的区别在这里,遇到await,异步方法调用结束,回到主线程执行,
//它后面的内容,以及子线程中内容,还在执行,事实应该是子线程中饭内容执行完,再执行await后面内容,
//同步方式编写异步
Console.WriteLine("This is ReturnTaskAsync Method End " + Thread.CurrentThread.ManagedThreadId);
}
}
}
阻塞的例子:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//官方说的很清楚,await只能用在以上类型的操作数,
private async void button1_Click(object sender, EventArgs e)
{
//string result = MethodAsync().Result;
string result = await MethodAsync();
}
//分析死锁原因,它必须等待MethodAsync执行完毕,等待return,但是这个异步方法的return,是在主线程中执行,
//首先主线程阻塞,因为它要等待异步方法执行完,但是异步方法本身return,需要在主线程中完成,但这时主线程阻塞起来,也就是子线程永远等不到执行完
private void button2_Click(object sender, EventArgs e)
{
//这里死锁形成
textBox1.Text = MethodAsync().Result;
//可以再包裹一个子线程,在子线程结束完成进行一些处理,然而明显更加麻烦,
//还不如将封装的方法,直接放在这个线程中
//var t = Task.Run(() =>
//{
// return MethodAsync().Result;
//});
//t.ContinueWith((task =>
//{
// textBox1.Text = task.Result;
//}));
}
private async Task MethodAsync()
{
Task ResultFromTimeConsumingMethod = TimeConsumingMethod();
return await ResultFromTimeConsumingMethod;
}
//这个函数就是一个耗时函数,可能是IO操作,也可能是cpu密集型工作。
private Task TimeConsumingMethod()
{
var task = Task.Run(() =>
{
Thread.Sleep(5000);
return "Hello I am TimeConsumingMethod 1";
});
return task;
}
//自身就是一个异步方法
private async void button3_Click(object sender, EventArgs e)
{
var t = Task.Run(() =>
{
Thread.Sleep(5000);
return "Hello I am TimeConsumingMethod 3";
});
textBox1.Text = await t;
}
private void button4_Click(object sender, EventArgs e)
{
var t = Task.Run(() =>
{
Thread.Sleep(5000);
//放在这里失去,执行某个耗时过程得到结果,再赋值的意义
//textBox1.Text = "测试";
return "Hello I am TimeConsumingMethod 4";
});
//一个不错处理方式,将异步完成里面,添加赋值即,continueWith方法
t.ContinueWith((task => {
textBox1.Text = task.Result;
}));
//这里用Result暂时阻塞
//textBox1.Text = t.Result;
}
}
}