ASYNC and AWAIT

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

 

 

一天一分享,身體好健康。

該追究的不是過去的原因,而是現在的目的。