1. 初始化共享资源不管同时有多少线程调用 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> 对象,就是一个用异步方式得到的整数值。 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 对象 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() 也会被触发订阅。 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
尽量不要用 Result 或 Wait 把异步代码强制转换为同步代码。
|