博客園首發-自定義awaiter


假設閱讀本文的您已經對await/async有了一定了解。

.net中的新關鍵字await出來很久,剛接觸時心里對它非常抵觸,習慣了用過去的異步模型,感覺已經足夠我實現各種想要的功能,認為新出的await簡化了代碼,必然犧牲代碼的靈活性。這段時間公司項目不忙,周圍同事一起討論await用法,討論之間發現大家對其理解各不相同,這……激發我對await的深入研究的興趣。

首先說幾點常見的誤區,隨后介紹下await的高級用法。

誤區一:帶有async/await關鍵字的方法難道就是異步方法了嗎?

答案是:不是,有async關鍵字只是告訴編譯器我這個方法可以用await。實際上沒發起任何異步操作,

即使再有await也不是用來發起異步操作的。await僅僅是一個高級的handler。

誤區二: 我們通常只用await后跟一個方法例如:

      await SomeMethod();

   await不是用來調用方法的前綴。它是用來接收一個異步的返回值例如Task;

可以這樣寫:

var result=SomeMethod();

await result;

誤區三:await的控制流:微軟做了大量的努力通過await/async關鍵字,把之前的前后顛倒,跳來跳去的控制流串成我們代碼所寫的順序,

為了增強閱讀性,讓我們易於調試和開發。很多人(包括之前的我)真的以為帶有async的函數就是一個線程的順序執行的控制流。

實際上不是那樣。副一張圖介紹下。

實線箭頭為主線程的控制流,虛線箭頭為接收異步回調開啟的新線程的控制流。再看一個例子。

現在我們避免了上面的誤區可以更自由的發揮了。

最后一個介紹才是本文的重頭戲:

await的高級應用,自定義awaiter。讓我們的await可以await一切。

之前我們的await只能這樣用的:

await SomeMethod();

await Task.delay(5000);

或者

var result=SomeMethod();

await result;

大家有沒有這樣用過?

await 5000;//延遲5秒;等於awaitTask.delay(5000);

string s=await "http://www.hanselman.com/blog/NuGetPackageOfTheWeek13PortableHttpClientMakesPortableLibrariesMoreUseful.aspx";//get這個地址的html;

甚至可以這樣

Task.Run(()=>{

string s="hello";

this.SomeTextBlock.Text=s;

});

上面的寫法肯定是會報錯的,因為不能從其他線程給UI線程的控件賦值。

我自己封裝了一個線程同步的方法:

Task.Run(()=>{

string s="hello";

await this.SomeTextBlock;

this.SomeTextBlock.Text=s;

});

await一下這個想要被操作的控件就可以賦值了。

 是不是很過癮?

我來一步一步告訴大家怎么樣實現。

await接收的是TaskAwaiter 或TaskAwaiter <T> 的返回值 所以我們定義一個擴展方法可以讓await當做自定義類型的前綴。

 public static class AwaitHelper
    {
        //public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan)
        //{

        //    return TaskEx.Delay(timeSpan).GetAwaiter();
        //}
        public static TaskAwaiter GetAwaiter(this int ms)
        {
            return Task.Delay(ms).GetAwaiter();
        }

        public static TaskAwaiter GetAwaiter(this string s)
        {
            return new Task(() => { }).GetAwaiter();
        }

        public static AllanControlAwaiter GetAwaiter(this DependencyObject control)
        {
            return new AllanControlAwaiter(control);
        }
    }

看到這里我們就知道

await 5000;//延遲5秒;

是怎么實現的吧。

下面我們繼續封裝一個AllanControlAwaiter實現線程同步操作

 public class AllanControlAwaiter : INotifyCompletion
    {
        private readonly DependencyObject m_control;
        private bool _IsCompleted = false;
        public AllanControlAwaiter(DependencyObject control)
        {
            m_control = control;
        }

        public bool IsCompleted
        {
            get { return _IsCompleted; }
        }

        public void OnCompleted(Action continuation)
        {
            var result = m_control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => continuation());
            result.AsTask().Wait();
            _IsCompleted = true;
        }

        public void GetResult() { }


    }

到這里大功告成。

快去體驗一下可以await一切的感覺吧!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM