ASYNC and AWAIT
前言
async 和 await 建立在 工作式非同步模式(TPA) 之上。 async是一個修飾詞,加入async修飾詞的function表示這個function是可以被等待的,如果有回傳值則一定要是傳Task。
而加入了async修飾詞的function才可以使用await運算式,compiler只要看到await 運算式 則會立即回到呼叫此function的主要function,主要function不必等到被呼叫的function執行結束即可取得控制權。
為了不造成混淆,先介紹Async的一些特性
static void Main(string[] args)
{
Main main = new Main();
Task<int> sum = main.GetSumAsync(1, 10);
Console.WriteLine(sum.Result); // output:11
}
public class Main
{
public async Task<int> GetSumAsync(int a, int b)
{
return a + b;
}
}
重點一:我們先看GetSumAsync ,使用async修飾詞,所以一定要回傳Task 或者 Task<TResult>。
重點二:雖然我簽章的回傳值是Task<int>,但是實際上程式回傳int,這是因為async的關係所以可以直接回傳int型別。
重點三:看執行端,呼叫GetSumAsync(1,10),只能取到Task<int> 型別,要取得實際的回傳值,則要使用.Result。
以上是async的基本使用方式。
根據前言提到,加入了async修飾詞的function才可以使用await運算式。
static void Main(string[] args)
{
Main main = new Main();
Task<int> sum = main.GetSumAsync(1, 10);
Console.WriteLine(sum.Result);
}
public class Main
{
public async Task<int> GetSumAsync(int a, int b)
{
int result = await GetDoubleSumAsync(a, b);
return result;
}
private async Task<int> GetDoubleSumAsync(int a, int b)
{
return a + a + b + b;
}
}
重點一:await只能用在有加入修飾詞async的function裡面,而且只能await 有修飾詞async的function
重點二:為了讓重點一的兩個條件成立,所以我新增一個async Task<int> GetDoubleSumAsync,讓GetSumAsync裡面使用await
重點三:注意int result = await GetDoubleSumAsync(a, b); ,GetDoubleSumAsync回傳明明是Task<int>,為什麼這邊卻是用int? 這是因為,使用await運算式回傳的是TResult的型別,而不是回傳Task<int>。
以上為await的基本用法。
接下來則要Demo,此程式跑的順序,以及執行時候的當下執行緒是否會變動
static void Main(string[] args)
{
Main main = new Main();
Console.WriteLine("步驟 1 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
Task<int> sum = main.GetSumAsync(1, 10);
Console.WriteLine("步驟 5 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("結果:" + sum.Result); // output : 22
Console.WriteLine("步驟 6 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
Console.Read();
}
public class Main
{
public async Task<int> GetSumAsync(int a, int b)
{
Console.WriteLine("步驟 2 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
int result = await GetDoubleSumAsync(a, b);
Console.WriteLine("步驟 4 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
return result;
}
private async Task<int> GetDoubleSumAsync(int a, int b)
{
Console.WriteLine("步驟 3 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
return a + a + b + b;
}
}
結果:
竟然是,按照程式順序執行,而且是同一條執行緒!? 這結果告訴我們一件事情
Async/Await 不會建立執行緒。Async/Await 不會建立執行緒。Async/Await 不會建立執行緒
太重要了,所以要說三次。 詳細說明請參考這裡。
使用Task.Run
要讓Async/Await 發揮真正的功能,還需要用到Task.Run()
就讓我們使用Task.Run() 改寫一下,這邊只要改寫GetDoubleSumAsync就好,將return 改用await + Task.Run 建立執行緒。
static void Main(string[] args)
{
Main main = new Main();
Console.WriteLine("步驟 1 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
Task<int> sum = main.GetSumAsync(1, 10);
Console.WriteLine("步驟 5 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("結果:" + sum.Result); // output : 22
Console.WriteLine("步驟 6 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
Console.Read();
}
public class Main
{
public async Task<int> GetSumAsync(int a, int b)
{
Console.WriteLine("步驟 2 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
int result = await GetDoubleSumAsync(a, b);
Console.WriteLine("步驟 4 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
return result;
}
private async Task<int> GetDoubleSumAsync(int a, int b)
{
return await Task.Run(() =>
{
Thread.Sleep(500);
Console.WriteLine("步驟 3 => ThreadID : " + Thread.CurrentThread.ManagedThreadId);
return a + a + b + b;
});
}
}
備註: Task.Run 已經可以使用await 運算式了,所以實際上可以把GetDoubleSum的method拿掉。 這段是配合之前的程式碼,所以先這樣寫。
結果:
各位觀眾,多了一條執行緒出來了,而且程式執行的順序,也改變了。 步驟2的時候,下一行遇到await,則程式會跳回呼叫的function,所以會先執行步驟 5,符合前言所說 complier只要看到await 運算式 則會立即回到呼叫此function的主要function。 程式執行的流程,請參考這裡,講的很詳細。
其實前言就破題了,Async/Await就這些觀念,有這些觀念後,之後設計複雜的非同步程式,會更得心應手。
參考:
http://www.codeproject.com/Articles/805748/Understanding-await-and-async-through-code
http://www.codeproject.com/Articles/1054993/async-await-What-You-Should-Know-Updated
https://msdn.microsoft.com/zh-tw/library/hh191443.aspx
一天一分享,身體好健康。
該追究的不是過去的原因,而是現在的目的。