Csharper Async和Await
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后面的內容,都會讓主線程來執行;
因此造成了主線程得死鎖。

浙公網安備 33010602011771號