Async和Await的学习
AsyncAwait语法解读
它是一个语法糖:编译器提供的便捷功能
async 是用来修饰方法,如果单独出现,方法会警告,没有什么作用
await在方法体内部,只能放在async修饰的方法内,必须放在task前面
async/await方法里面如果没有返回值,默认返回一个Task,或者void(推荐用Task,而不是void,因为这样才能await/wait)
带async+await后,返回值要多一层Task<>
不使用await/async的代码进行运行
public static void TestShow()
{
Test();
}
public static void Test()
{
Console.WriteLine($"当前主线程id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
{
NoReturnNoAwait();
}
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Console.Read();
}
private static async void NoReturnNoAwait()
{
//主线程执行
Task task = Task.Run(() =>//启动新线程完成任务
{
Thread.Sleep(1000);
Console.WriteLine($"NoReturnNoAwait Sleep3000 before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"NoReturnNoAwait Sleep3000 after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
//主线程执行
Console.WriteLine($"NoReturnNoAwait Sleep after Task,ThreadId={Thread.CurrentThread.ManagedThreadId}");
}
}
运行后的结果
运行另外一个函数并不适用async:
public static void Test()
{
//Console.WriteLine($"当前主线程id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
//{
// NoReturnNoAwait();
//}
{
Task t = NoReturn();
t.Wait();//主线程在这里就可以登录
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(300);
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")} i={i}");
}
}
}
}
private static Task NoReturn()
{
//主线程执行
Console.WriteLine($"NoReturn Sleep before await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
TaskFactory taskFactory = new TaskFactory();
Task task = taskFactory.StartNew(() =>
{
Console.WriteLine($"NoReturn Sleep3000 before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(3000);
Console.WriteLine($"NoReturn Sleep3000 after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
//像什么?continuwith
//task.ContinueWith(t =>
//{
// Console.WriteLine($"NoReturn Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
//});
return task;
}
使用async和await关键字的方法
public static void Test()
{
{
Task t = NoReturnTask();
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
}
}
private static async Task NoReturnTask() //在async/await方法里面如果没有返回值,默认返回一个Task
{
//这里还是主线程的id
Console.WriteLine($"NoReturnTask Sleep before await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
Task task = Task.Run(() =>
{
Console.WriteLine($"NoReturnTask Sleep3000 before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"NoReturnTask Sleep3000 after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
await task;
Console.WriteLine($"NoReturnTask Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
}
可以观察到我们的代码都是并行的。
- await: 线程遇到awai就返回,不阻塞:并发执行
- 做到控制顺序
其实就是以写同步方法 的方式;来完成多线程:--可以控制顺序 - 主线程和子线程是并发执行
- 有点类似于一个回调
- 有async/await 如果没有返回值,就直接返回一个Task
特点:
l. 可以多线程并发执行,可以控制代码的执行顺序一一做到了严格控制代码的执行顺序;
2.执行的特点:当前执行的线程,调用的有asyncawait修饰的函数,且没有await修饰,进入函数后,遇到await,就直接返回执行调用当前函数后面的代码:如果当前调用也用了await修饰,就严格按照顺序执行:不存在遇到await回去继续往后执行:
3.await启动线程的后面的内容,又会启动一个新的线程来继续往后执行;以此往复;
AsyncAwait底层源码解读(状态机)
async和await在底层是一个状态机(状态模式),类似红绿灯,红灯:停,绿灯:行,按照条件执行,像switch
示例代码
public class AwaitAsyncILSpy
{
public static void Show()
{
Console.WriteLine($"start1 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Async();
Console.WriteLine($"aaa2 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
public static async void Async()
{
Console.WriteLine($"ddd5 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
await Task.Run(() =>
{
Thread.Sleep(500);
Console.WriteLine($"bbb3 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
});
Console.WriteLine($"ccc4 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
}
}
使用Ilspy反编译以后
看到IL反汇编出来的代码,我们可以知道:
- 实例化状态机
- 把状态机实例交给一个build去执行
- 整理线程的上下文
- stateMachine.MoveNext(); 调用MoveNext方法
- MoveNext如何执行:先获取一个状态 ---继续往后执行
- 如果有异常---一抛出异常--把状态重置为-2
- 如果没有异常,把状态重置重置为-2
- SetResult();---把结果包裹成一个Tsak
Winform中使用async/await
这里创建一个winform的程序并实现了2个按钮
同步按钮得代码:
private void btnSync_Click(object sender, EventArgs e)
{
Debug.WriteLine($"**********************************btnSync_Click******************************************");
Debug.WriteLine($"This is btnSync_Click Start,ThreadId={Thread.CurrentThread.ManagedThreadId}");
var task = this.CalculationAsync(1_000_000);
long lResult = task.Result;
Debug.WriteLine($"This is btnSync_Click End,ThreadId={Thread.CurrentThread.ManagedThreadId}");
this.textSync.Text = lResult.ToString();
}
异步按钮得代码:
/// <summary>
///异步方法: 正常执行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnAsync_Click(object sender, EventArgs e)
{
Debug.WriteLine($"**********************************btnAsync_Click******************************************");
Debug.WriteLine($"This is btnAsync_Click Start,ThreadId={Thread.CurrentThread.ManagedThreadId}");
long lResult = await this.CalculationAsync(1_000_000);
Debug.WriteLine($"This is btnAsync_Click End,ThreadId={Thread.CurrentThread.ManagedThreadId}");
this.textAsyncResult.Text = lResult.ToString();
}
对应计算得代码:
private async Task<long> CalculationAsync(long total)
{
var task = await Task.Run(() =>
{
Debug.WriteLine($"This is CalculationAsync Start,ThreadId={Thread.CurrentThread.ManagedThreadId}");
long lResult = 0;
for (int i = 0; i < total; i++)
{
lResult += i;
}
Debug.WriteLine($"This is CalculationAsync End,ThreadId={Thread.CurrentThread.ManagedThreadId}");
return lResult;
});
return task;
}
当点击同步按钮得时候就会遇到一个问题,程序卡死了,为什么会造成这个问题?
- 主线程要等待 Winform特殊设计:await后面的内容必须由主线程执行;
- 主线程在这儿也等待着在
- 主线程无暇分身导致死锁
更改控件的值,这里必须是(UI线程)主线程去执行;
每次执行都能成功,说明每次这里都是主线程来执行的;
跟Winform设计有关系;在Winform中,await后面的内容,都会让主线程来执行;
因此造成了主线程得死锁。
标签:await,Console,CurrentThread,Thread,ManagedThreadId,Await,WriteLine,Csharper,Asyn From: https://www.cnblogs.com/wenlong-4613615/p/18252655