3月
26
2019
分類:
最近更新:
2019-03-26
.NET 筆記 - async method and sync code; await and Task.Result
我用 .NET Core 實作一個呼叫 RESTful API 的簡單程式時,碰到一個小麻煩。 .NET Core 的 HttpClient 類提供的方法都是非同步方法 (async method)。
但我是在同步形式的程式碼用到 HttpClient 。我不想回頭去改程式碼加上 async Task
的宣告。所以我得要呼叫非同步方法,但用同步形式程式碼取得結果。
非同步形式程式碼 (asynchronous code)
先看一下非同步形式程式碼範例,取自 HttpClient Class Example。
static async Task Main()
{
// Create a New HttpClient object and dispose it when done, so the app doesn't leak resources
using (HttpClient client = new HttpClient())
{
// Call asynchronous network methods in a try/catch block to handle exceptions
try
{
HttpResponseMessage response =
await client.GetAsync("http://www.contoso.com/");
response.EnsureSuccessStatusCode();
string responseBody =
await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method below
// string responseBody = await client.GetStringAsync(uri);
Console.WriteLine(responseBody);
}
catch(HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ",e.Message);
}
}
}
針對回傳值為 Task<TResult>
的非同步方法,加上 await 關鍵字去接。而 await 會令 C# 編譯器拓展一段程式碼,插入 Task<TResult>.GetAwaiter()
,等待被呼叫方法的執行緒回傳結果。
同步形式程式碼 (synchronous code)
但在同步形式程式碼中,不能使用 await 關鍵字。此時,Task<TResult>.Result
提供了在同步形式程式碼中呼叫非同步方法的途徑。Task.Result
這個屬性取值器將擱置呼叫方的執行緒(就是自己現行執行緒),直到非同步方法傳回結果解除呼叫方執行緒的閒置狀態,再將結果存入 Result
屬性,繼續下一步。
將上例的非同步形式程式碼改成同步形式程式碼,如下:
static void Main()
{
using (HttpClient client = new HttpClient())
{
try
{
HttpResponseMessage response =
client.GetAsync("http://www.contoso.com/").Result;
response.EnsureSuccessStatusCode();
string responseBody =
response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
}
catch(HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ",e.Message);
}
}
}
首先, main()
的宣告不用加上 async Task
。
接著,因為非同步方法宣告的回傳值型別是 Task
,而我們要操作的正是 Task
的屬性 Result
,所以用方法串接語法接起來就好。例如 GetAsync().Result
。
Task<TResult>.Result
是平台特性而不是 C# 語言特性。在 .NET 平台上運作的其他程式語言,也適用這個模式處理 .NET Core 的非同步方法。