博客园首发自定义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一切的感觉吧!

原文地址:https://www.cnblogs.com/allanxyq/p/2975431.html