zoukankan      html  css  js  c++  java
  • 实用技巧

    >>返回《C# 并发编程》

    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 把异步代码强制转换为同步代码。

  • 相关阅读:
    盘古越狱工具在用户空间的行为
    hdu 5538 House Building(长春现场赛——水题)
    html 锚点定位
    OOP版电子词典
    有趣的JavaScript原生数组函数
    &lt;LeetCode OJ&gt; 121. /122. Best Time to Buy and Sell Stock(I / II)
    hadoop 出现FATAL conf.Configuration: error parsing conf file,异常
    IT痴汉的工作现状10-Sprint Planning
    2015 Astar Contest
    无法使用BIPublisher开发报表
  • 原文地址:https://www.cnblogs.com/BigBrotherStone/p/12251012.html
Copyright © 2011-2022 走看看