不管同时有多少线程调用 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>
对象,就是一个用异步方式得到的整数值。
不管有多少代码段同时调用 Value
, Task<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
想要在每次被订阅时就创建一个新的源 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()
也会被触发订阅。
在异步地检索数据时,需要对结果进行数据绑定(例如绑定到 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)); }}
异步初始化模式
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.
如果每次访问属性都会启动一次新的异步操作,那说明这个“属性”其实应该是一个方法。
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
尽量不要用 Result
或 Wait
把异步代码强制转换为同步代码。
联系客服