>>返回《C# 并发编程》html
无论同时有多少线程调用 GetSharedIntegerAsync
,这个工厂委托只会运行一次,而且全部线程都等待同一个实例。编程
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
调用中:async
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; } }
输出:this
[19] MySharedAsyncInteger
想要在每次被订阅时就建立一个新的源 observable 对象线程
Rx 库有一个操做符Observable.Defer
(初始化时会执行委托)
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
把异步代码强制转换为同步代码。