打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
实用技巧

1. 初始化共享资源

不管同时有多少线程调用 GetSharedIntegerAsync ,这个工厂委托只会运行一次,并且所有线程都等待同一个实例。

  • 实例在创建后会被缓存起来,以后所有对 Value 属性的访问都返回同一个实例。

public static void UtilShareRun(){    // 示例1: 100次并行调用,只输出一次,验证了 只被执行一次 和 线程安全性    Parallel.For(0, 100, (i, s) =>    {        UtilShare share = new UtilShare();        share.GetSharedIntegerAsync().Wait();    });    // 示例2: 显示出调度线程号的切换情况    // 示例3: 执行前已经调用了 share.GetSharedIntegerAsync()     // 那么后面无论是否设置 ConfigureAwait 后面是不会发生上下文切换的,因为已经是直接拿到结果了    // share.GetSharedIntegerAsync().Wait();    // AsyncContext.Run(async () =>    // {    //     UtilShare share = new UtilShare();    //     System.Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] before.");    //     await share.GetSharedIntegerAsync()    //       //.ConfigureAwait(false);    //       ;    //     System.Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] after.");    // });    }public class UtilShare{    static int _simpleValue;    static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(async () =>    {        System.Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}]");        await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);                // 只输出一次                System.Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] " + nameof(MySharedAsyncInteger));        return _simpleValue++;    });    public async Task GetSharedIntegerAsync()    {        int sharedValue = await MySharedAsyncInteger.Value;    }}

示例1 输出:

; 使用当前上下文调用[1]; 因为设置了 ConfigureAwait 导致上下文不延续,后面交给线程池线程执行[18] MySharedAsyncInteger

示例2 输出:

[1] before.[1][4] MySharedAsyncInteger; 因为 await share.GetSharedIntegerAsync();延续了上下文; 所以此处恢复了调用前是一个上下文; 如果设置为不延续,则此处线程号会是线程池线程[1] after.

示例3 输出:

; 第一次执行[1][4] MySharedAsyncInteger; 因为已经有结果了,后面不会造成上下文切换[1] before.[1] after.

本例中委托返回一个 Task<int> 对象,就是一个用异步方式得到的整数值。

  • 不管有多少代码段同时调用 ValueTask<int> 对象只会创建一次,并且每个调用都返回同一个对象

  • 每个调用者可以用 await 调用这个 Task 对象,(异步地)等待它完成

Lazy 委托中的代码会在当前同步上下文中运行。

如果有几种不同类型的线程会调用 Value(例如一个 UI 线程和一个线程池线程,或者两个不同的 ASP.NET 请求线程),那最好让委托只在线程池线程中运行。这实现起来很简单,只要把工厂委托封装在 Task.Run 调用中:

public static void UtilShareTaskRun(){    Parallel.For(0, 100, (i, s) =>    {        UtilShareTask share = new UtilShareTask();        share.GetSharedIntegerAsync().Wait();    });}public class UtilShareTask{    static int _simpleValue;    static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(() =>        Task.Run(async () =>        {            await Task.Delay(TimeSpan.FromSeconds(2));            // 只输出一次            System.Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] " + nameof(MySharedAsyncInteger));            return _simpleValue++;        })    );    public async Task GetSharedIntegerAsync()    {        int sharedValue = await MySharedAsyncInteger.Value;    }}

输出:

[19] MySharedAsyncInteger

2. Rx延迟求值

想要在每次被订阅时就创建一个新的源 observable 对象

  • 例如让每个订阅代表一个不同的 Web 服务请求。

Rx 库有一个操作符Observable.Defer (初始化时会执行委托)

  • 每次 observable 对象被订阅时,它就会执行一个委托。

  • 该委托相当于是一个创建 observable 对象的工厂

public static void UtilDeferRun(){    var invokeServerObservable = Observable.Defer(() => GetValueAsync().ToObservable());    invokeServerObservable.Subscribe(_ => { });    // invokeServerObservable.Subscribe(_ => { });    Thread.Sleep(2000);}static async Task<int> GetValueAsync(){    Console.WriteLine("Calling server...");    await Task.Delay(TimeSpan.FromMilliseconds(100));    Console.WriteLine("Returning result...");    return 13;}

输出:

Calling server...Returning result...

注意: 如果对 Defer 后的 observable 对象 await 或者 Wait() 也会被触发订阅。

3. 异步数据绑定

在异步地检索数据时,需要对结果进行数据绑定(例如绑定到 Model-View-ViewModel 设计模式中的 ViewModel)。

可以使用 AsyncEx 库中的 NotifyTaskCompletion 类:

class MyViewModel{    public MyViewModel()    {        MyValue = NotifyTaskCompletion.Create(CalculateMyValueAsync());    }    public INotifyTaskCompletion<int> MyValue { get; private set; }    private async Task<int> CalculateMyValueAsync()    {        await Task.Delay(TimeSpan.FromSeconds(10));        return 13;    }}

可以绑定到 INotifyTaskCompletion<T> 属性中的各种属性,如下所示:

<Grid>    <Label Content="Loading..."Visibility="{Binding MyValue.IsNotCompleted,Converter={StaticResource BooleanToVisibilityConverter}}"/>    <Label Content="{Binding MyValue.Result}"Visibility="{Binding MyValue.IsSuccessfullyCompleted,Converter={StaticResource BooleanToVisibilityConverter}}"/>    <Label Content="An error occurred" Foreground="Red"Visibility="{Binding MyValue.IsFaulted,Converter={StaticResource BooleanToVisibilityConverter}}"/></Grid>

也可以自己编写数据绑定的封装类代替 AsyncEx 库中的类。下面的代码介绍了基本思路:

class BindableTask<T> : INotifyPropertyChanged{    private readonly Task<T> _task;         public BindableTask(Task<T> task)    {        _task = task;        var _ = WatchTaskAsync();    }    private async Task WatchTaskAsync()    {        try        {            await _task;        }        catch { }        OnPropertyChanged("IsNotCompleted");        OnPropertyChanged("IsSuccessfullyCompleted");        OnPropertyChanged("IsFaulted");        OnPropertyChanged("Result");    }    public bool IsNotCompleted    {        get        {            return !_task.IsCompleted;        }    }    public bool IsSuccessfullyCompleted    {        get        {            return _task.Status == TaskStatus.RanToCompletion;        }    }    public bool IsFaulted { get { return _task.IsFaulted; } }    public T Result    {        get        {            return IsSuccessfullyCompleted ? _task.Result : default(T);        }    }    public event PropertyChangedEventHandler PropertyChanged;    protected virtual void OnPropertyChanged(string propertyName)    {        PropertyChangedEventHandler handler = PropertyChanged;        if (handler != null)            handler(this, new PropertyChangedEventArgs(propertyName));    }}

4. 异步构造

异步初始化模式

public static void AsyncConstructionRun(){    var task = Task.Run(async () =>    {        IMyFundamentalType instance = new MyFundamentalType();        System.Console.WriteLine("Instance created.");        var instanceAsyncInit = instance as IAsyncInitialization;        if (instanceAsyncInit != null)        {            await instanceAsyncInit.Initialization;            System.Console.WriteLine("Instance Initialized.");        }    });    task.Wait();}interface IMyFundamentalType { }interface IAsyncInitialization{    Task Initialization { get; }}class MyFundamentalType : IMyFundamentalType, IAsyncInitialization{    public MyFundamentalType()    {        Initialization = InitializeAsync();    }    public Task Initialization { get; private set; }    private async Task InitializeAsync()    {        System.Console.WriteLine("MyFundamentalType initializing.");        // 对这个实例进行异步初始化。        await Task.Delay(TimeSpan.FromSeconds(1));        System.Console.WriteLine("MyFundamentalType initialized.");    }}

输出:

MyFundamentalType initializing.Instance created.MyFundamentalType initialized.Instance Initialized.

可以对这种模式进行扩展,将类和异步初始化结合起来。下面的例子定义了另一个类,它以前面建立的 IMyFundamentalType 为基础:

public static void AsyncConstructionsRun(){    AsyncInitialization.WhenAllInitializedAsync(new MyComposedType(new MyFundamentalType()), new MyComposedType(new MyFundamentalType())).Wait();}class MyComposedType : IAsyncInitialization{    private readonly IMyFundamentalType _fundamental;    public MyComposedType(IMyFundamentalType fundamental)    {        _fundamental = fundamental;        Initialization = InitializeAsync();    }    public Task Initialization { get; private set; }    private async Task InitializeAsync()    {        System.Console.WriteLine("MyComposedType initializing.");        // 如有必要,异步地等待基础实例的初始化。        var fundamentalAsyncInit = _fundamental as IAsyncInitialization;        if (fundamentalAsyncInit != null)            await fundamentalAsyncInit.Initialization;        // 做自己的初始化工作(同步或异步)。...        System.Console.WriteLine("MyComposedType initialized.");    }}public static class AsyncInitialization{    public static Task WhenAllInitializedAsync(params object[] instances)    {        return Task.WhenAll(instances.OfType<IAsyncInitialization>().Select(x => x.Initialization));    }}

输出:

MyFundamentalType initializing.MyComposedType initializing.MyFundamentalType initializing.MyComposedType initializing.MyFundamentalType initialized.MyComposedType initialized.MyFundamentalType initialized.MyComposedType initialized.

5. 异步属性

如果每次访问属性都会启动一次新的异步操作,那说明这个“属性”其实应该是一个方法。

public static void UtilPropRun(){    var instance = new AsyncProp();    var task = Task.Run(async () =>    {        var propValue = await instance.Data.Task;        System.Console.WriteLine($"PropValue:{propValue}");    });    task.Wait();}class AsyncProp{    // 作为一个缓存的数据。    public AsyncLazy<int> Data { get { return _data; } }    private readonly AsyncLazy<int> _data = new AsyncLazy<int>(async () =>    {        await Task.Delay(TimeSpan.FromSeconds(1));        return 13;    });}

输出:

PropValue:13

尽量不要用 ResultWait 把异步代码强制转换为同步代码。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
.Net 4.5 异步编程初试(async和await)
C#中的异步编程--探索await与async关键字的奥妙之处,原来理解和使用异步编程可以这么简单
C#中Task任务和Async、Await异步非阻塞方式
异步编程初探async和await
探索c#之Async、Await剖析
C#语法——await与async的正确打开方式
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服