C# 非同步操作 async await

sparkdev發表於2016-10-08

在程式設計的過程中,我們會遇到很多需要非同步操作的場景。比如要下載一個檔案,如果使用同步的方式進行下載,那麼UI操作就會被卡住,這時最好能夠使用非同步的方式進行下載。在C#中,很早就開始支援非同步的操作了,只不過早期的方法寫起來有些麻煩。到了C# 5.0的時候,直接新增了兩個關鍵字:async 和 await,這下對非同步操作的支援可謂是達到了完美。下面我們就通用一個簡單的 demo 看一下它們的基本用法。

下載一個檔案

我們先看看用同步的方式下載一個檔案:

private void btnOldDownload_Click(object sender, EventArgs e)
{
    using(WebClient wc = new WebClient())
    {
        // 我們嘗試去下載 python 的安裝包。
        wc.DownloadFile("https://www.python.org/ftp/python/3.5.2/python-3.5.2-amd64.exe", "python.exe");
    }
    lbMessage.Text = "下載完成。";
}

在執行 DownloadFile 方法的過程中,UI執行緒被卡死,整個的下載過程中應用程式都處於沒響應的狀態。這當然是不能接受的,所以我們應該使用下面的非同步方式進行檔案的下載。

非同步下載檔案

我們先看一下使用早期的非同步程式碼去下載檔案:

private void OldAsyncDownload_Click(object sender, EventArgs e)
{
    using (WebClient wc = new WebClient())
    {
        // 我們嘗試去下載 python 的安裝包。
        // 下載完成時會有事件通知。
        wc.DownloadFileCompleted += Wc_DownloadFileCompleted;
        wc.DownloadFileAsync(new Uri("https://www.python.org/ftp/python/3.5.2/python-3.5.2-amd64.exe"), "python.exe");
    }
}
private void Wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    lbMessage.Text = "下載完成。";
}

好吧,事情變得稍微有點複雜了。一個簡單的下載邏輯居然被分隔到了兩個方法中!在第一個方法中掛載 DownloadFileCompleted 事件,然後啟動下載。下載完成後透過 DownloadFileCompleted 事件處理函式進行通知。如果您能夠習慣於這樣的寫法,其實C# 早期提供的非同步程式設計介面已經足夠用了,因為這也正是非同步操作真正的實現方式。

但是,作為程式設計師,我們是不會滿足的,永遠不會…

async await 方式

在C# 5.0 中引入了 async 和 await 關鍵字,它們使得非同步操作的實現更加簡單、容易!

private async void btnMyAsync_Click(object sender, EventArgs e)
{
    using (WebClient wc = new WebClient())
    {
        // 我們嘗試去下載 python 的安裝包。
        Task task = wc.DownloadFileTaskAsync("https://www.python.org/ftp/python/3.5.2/python-3.5.2-amd64.exe", "python.exe");
        // 可以在這裡執行程式碼。
        await task;
    }
    lbMessage.Text = "下載完成。";
}

注意程式碼中 async 和 await 關鍵字的位置,這樣程式碼就是以非同步方式去執行下載邏輯,整個過程中UI 操作不會被阻塞掉。是不是很神奇呢?

這段程式碼最大的魔力在於它和同步下載檔案的程式碼幾乎相同!每個 人都喜歡寫順序執行的程式碼,這樣的程式碼邏輯也最清晰。async 和 await 的最大優勢就是把人腦感覺簡單的程式碼轉換為真正的非同步操作邏輯。

相關文章