异步与Windows应用程序
UWP应用程序中,在UI线程中调用
await之后,当异步方法返回时,将默认返回到UI线程中。这允许直接访问UI元素。
UWP应用程序,定义了一个同步上下文
private async void OnStartAsync(object sender, RoutedEventArgs e)
{
text1.Text = $"UI thread: {GetThread()}";
await Task.Delay(1000);
text1.Text += $"\n after await: {GetThread()}";
}
//UWP应用程序中,Thread类是无效的,可以用Environment类
private string GetThread() => $"thread: {Environment.CurrentManagedThreadId}";
输出:
UI thread: thread 3
UI thread: thread 3
如何不使用同步上下文
- ConfigureAwait(continueOnCapturedContext: true) 就可以不使用同步上下文
private async void OnStartAsyncConfigureAwait(object sender, RountedEventArgs e)
{
text1.Text = $"UI thread: {GetThread()}";
string s = await AsyncFunction().ConfigureAwait(continueOnCapturedContext: true);
// after await, with continueOnCapturedContext true, we are back in the UI thread
text1.Text += $"\n{s}\nafter await: {GetThread()}";
async Task<string> AsyncFunction()
{
string result = $"\nasync function: {GetThread()}";
await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);
result += $"\nasync function after await : {GetThread()}";
try
{
text1.Text = "this is a call from the wrong thread";
return "not reached";
}
catch (Exception ex) when (ex.HResult == -2147417842)
{
return result;
//we know it's the wrong thread
//don't access UI elements from the previous try block
}
}
}
输出:
UI thread: thread 3
async function: thread 3
async function after await: thread 6
after await : thread 3
Dispatcher
有时候,在后台线程里访问 UI 元素,不容易。可以用
Dispatcher
- UI元素的基类
DependencyObject类里,有个Dispatcher属性 Dispatcher属性,所返回的CoreDispatcher对象,可以切换到UI线程CoreDispatcher对象的RunAsync方法,会在UI线程中再次运行传递进来的lamda表达式
private async void OnStartAsyncConfigureAwait(object sender, RountedEventArgs e)
{
text1.Text = $"UI thread: {GetThread()}";
string s = await AsyncFunction();
text1.Text += $"\n{s}\nafter await: {GetThread()}";
async Task<string> AsyncFunction()
{
string result = $"\nasync function: {GetThread()}";
await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);
result += $"\nasync function after await : {GetThread()}";
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//因为回到了UI线程,UI线程没被阻塞,所以这一句反而可以执行得早
text1.Text = $"\nasync function switch back to the UI thread: {GetThread()}";
});
return "not reached";
}
}
输出:
UI thread: thread 3
async function switch back to the UI thread: thread 3
async function: thread 3
async function after await: thread 6
after await : thread 3
IAsyncOperation
Windows运行库定义的那些异步方法,不返回
Task或ValueTask。而是返回一个IAsyncOperation对象。
- 那些异步方法,例如:MessageDialog的ShowAsync()方法
- 我们依然可以
awaitWindows运行库定义的那些异步方法- 因为,当使用
await关键字时,IAsyncOperation会自动转换为Task - 而且,我们还可以显式调用
AsTask扩展方法,将IAsyncOperation转换为Task
- 因为,当使用
避免死锁
在
Task上,一起使用Wait和async是很危险的。在使用同步上下文时,这很容易导致死锁。
private void OnStartDeadlock(object sender, RouteEventArgs e)
{
//Wait()方法,阻塞调用线程,直到任务完成
DelayAsync().Wait();
}
private async Task DelayAsync()
{
//Windows应用程序是使用同步上下文的,因此下面这句await执行完,会回到调用线程
//然而调用线程被Wait()阻塞,不可用
//因此,产生了死锁
await Task.Delay(1000);
}
应该避免,在使用同步上下文的应用程序中,同时使用Wait和await
UWP应用程序
- 需要打开“开发人员模式”
- 这样就可以运行不是从Windows Store中安装的应用程序
- 并为“开发人员模式”添加一个Windows包