>>返回《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
把異步代碼強制轉換爲同步代碼。