不管同时有多少线程调用 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> MySharedAsyncInteger = new Lazy<Task>(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 对象,就是一个用异步方式得到的整数值。
- 不管有多少代码段同时调用 Value , Task 对象只会创建一次,并且每个调用都返回同一个对象
- 每个调用者可以用 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> MySharedAsyncInteger = new Lazy<Task>(() => 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] MySharedAsyncInteger2. 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 TaskGetValueAsync() { 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 INotifyTaskCompletionMyValue { get; private set; } private async TaskCalculateMyValueAsync() { await Task.Delay(TimeSpan.FromSeconds(10)); return 13; } }
可以绑定到 INotifyTaskCompletion 属性中的各种属性,如下所示:
也可以自己编写数据绑定的封装类代替 AsyncEx 库中的类。下面的代码介绍了基本思路:
class BindableTask: INotifyPropertyChanged { private readonly Task_task; public BindableTask(Tasktask) { _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().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 AsyncLazyData { get { return _data; } } private readonly AsyncLazy_data = new AsyncLazy(async () => { await Task.Delay(TimeSpan.FromSeconds(1)); return 13; }); }
输出:
PropValue:13
尽量不要用 Result 或 Wait 把异步代码强制转换为同步代码。