1.await/async簡述
- await/async是C#保留關鍵字,通常成對出現
- async修飾方法,可以單獨出現,對原來的方法沒有任何改變,但會警告
- await在方法體,只能出現在task/async方法前面,單獨使用await會報錯
- async位於方法修飾符之後,返回類型之前
2.相關例子
2.1.單獨使用async
- 代碼如下:
/// <summary>
///方法帶async,但方法體中Task前面不帶await(與普通方法執行一樣,只不過會警告)
/// </summary>
static async void AsyncNoAwait()
{
Console.WriteLine($"AsyncNoAwait start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Task.Run(() =>
{
Console.WriteLine($"AsyncNoAwait--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"AsyncNoAwait--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine($"AsyncNoAwait end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
- 在控制檯輸出如下:
- 結論:使用方式與普通方法無異,只是會有警告
2.2.await和async同時使用
await在方法內部位於Task前面,當前線程遇到await則跳出方法,然後繼續執行,而await Task.Run()之後的代碼都要等Task執行完成之後執行,效果與Task.Run()的回調一樣
- 代碼如下:
static void AsyncAwaitTest()
{
Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//線程執行到AsyncWithAwait方法裏面的await語句之後,跳出此方法,繼續之後後面的代碼
AsyncWithAwait();
Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// await/async同時使用
/// 此await的作用就是讓當前線程不執行後面的代碼,而是跳出方法體,去執行調用次方法後面的代碼,
/// 而await後面的代碼在await 異步任務執行完成之後調用,類似回調
/// </summary>
static async void AsyncWithAwait()
{
Console.WriteLine($"AsyncWithAwait start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//執行到此處,當前線程就會跳出當前方法
await Task.Run(() =>
{
Console.WriteLine($"AsyncWithAwait--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"AsyncWithAwait--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
//此處的輸出等上面的Task.Run 執行結束之後執行
Console.WriteLine($"AsyncWithAwait end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
- 控制檯輸出如下:
- 結論:當前線程1執行遇到await,不執行await後面的代碼,而await Task.Run()新開一個線程3執行lambda,所以之後兩個線程是併發執行的,互不干擾,只不過是先後順序而已,至於Task.Run()後面的代碼,是在Task.Run()執行完成之後執行的,可能會是當前線程也可能會新開一個線程
2.3.async方法上加多Task
- 代碼如下:
static void AsyncAwaitTest()
{
Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//線程執行到AsyncWithAwait方法裏面的await語句之後,跳出此方法,繼續之後後面的代碼
Task task = TaskAsyncNoRerturn();
//wait會阻塞當前線程,直到TaskAsyncNoRerturn方法中await task後面的同步方法執行完成
task.Wait();
Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 方法帶Task,但方法沒有返回值
/// 返回的Task包含了整個調用的方法,如果使用Task.wait(),將等待方法同步線程執行完畢
/// </summary>
/// <returns></returns>
static async Task TaskAsyncNoRerturn()
{
Console.WriteLine($"TaskAsyncNoRerturn start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Task task = Task.Run(() =>
{
Console.WriteLine($"TaskAsyncNoRerturn--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"TaskAsyncNoRerturn--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
await task; //方法聲明中的Task與當前task沒有任何關係
//調用此方法,如果使用返回值task.wait()阻塞線程,將等到後面同步代碼執行完畢纔會跳過task.wait()
for (int i = 0; i < 2; i++)
{
Console.WriteLine($"{i} ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
Console.WriteLine($"TaskAsyncNoRerturn end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
- 控制檯輸出如下:
- 結論:在async方法中加入Task並不是指方法需要return,而是調用之後會自動返回一個Task類型的對象(暫時作爲 task),調用task.wait()之後,會阻塞當前線程,直到async方法內部的同步代碼執行完成
備註:async方法內部的同步代碼:指的是在await Task.Run()之後的代碼是沒有再創建任何線程去執行的代碼
2.4.帶返回值的async方法
方法聲明:訪問修飾符 async Task<返回值類型> 方法名
例子:public async Task<int> Method
- 代碼如下:
static void AsyncAwaitTest()
{
Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Task<int> task = TaskAsyncWithRerturn();
//調用task.Result獲取返回值,會阻塞當前線程,指定返回值計算完成
Console.WriteLine($"TaskAsyncWithRerturn的返回值:{task.Result}");
Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// TaskAsyncWithRerturn方法返回int類型的值
/// 調用TaskAsyncWithRerturn返回,會返回一個Task<int>對象
/// 調用者想要獲取返回值,就得等task線程計算完畢,就是會阻塞調用線程
/// </summary>
/// <returns></returns>
static async Task<int> TaskAsyncWithRerturn()
{
Console.WriteLine($"TaskAsyncWithRerturn start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
int result = 0;
Task task = Task.Run(() =>
{
Console.WriteLine($"TaskAsyncWithRerturn--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 200; i++)
{
result += i;
Thread.Sleep(10);
}
Console.WriteLine($"TaskAsyncWithRerturn--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
await task;
Console.WriteLine($"TaskAsyncWithRerturn end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
return result;
}
- 控制檯輸出如下:
- 結論:調用帶返回值的async方法,如果要獲取其返回值,就會阻塞當前線程,直到async方法中的返回值計算完畢