【发布时间】:2020-11-05 04:49:42
【问题描述】:
我有一个显示奇怪行为的异步代码的最小示例。这是沙盒代码,更适合尝试更好地理解异步 --
private async Task ExhibitStrangeBehaviorAsync()
{
async Task TaskA()
{
await Task.Run(async () =>
{
throw new Exception(nameof(TaskA));
await Task.Yield();
});
}
async Task TaskB()
{
await Task.Run(() =>
{
throw new Exception(nameof(TaskB));
});
}
var tasks = new List<Task>
{
TaskA(),
TaskB(),
};
var tasksTask = Task.WhenAll(tasks);
try
{
await tasksTask;
}
catch
{
Debug.WriteLine(tasksTask.Exception.Message);
}
}
此代码会间歇性地挂起。我想更好地理解为什么。我目前的猜测是间歇性是由于聚合任务的无序执行和/或来自Asynchronous Programming 的这一行:
LINQ 中的 Lambda 表达式使用延迟执行,这意味着代码最终可能会在您不期望的时候执行。
TaskA 属于这一类。
如果 TaskB 和 Task.Runs 是异步 lambda,或者本地 Task 函数都不包含 Task.Run,则代码似乎不会挂起,例如
async Task TaskA()
{
//await Task.Run(async () =>
//{
throw new Exception(nameof(TaskA));
await Task.Yield();
//});
}
async Task TaskB()
{
//await Task.Run(() =>
//{
throw new Exception(nameof(TaskB));
//});
}
谁能解释一下这里发生了什么?
编辑
这是在 UI 线程的上下文中执行的,特别是在 Xamarin.Forms 应用程序的上下文中。
编辑 2
这是另一个直接从 Xamarin.Forms OnAppearing 生命周期方法运行的变体。我不得不稍微修改TaskA/B,尽管它们也与上面的原始设置打破了这种方式。
protected async override void OnAppearing()
{
base.OnAppearing();
async Task TaskA()
{
await Task.Run(async () =>
{
throw new InvalidOperationException();
await Task.Delay(1).ConfigureAwait(false);
}).ConfigureAwait(false);
}
async Task TaskB()
{
await Task.Run(() => throw new ArgumentException()).ConfigureAwait(false);
}
var tasks = new List<Task>
{
TaskA(),
TaskB(),
};
var tasksTask = Task.WhenAll(tasks);
try
{
await tasksTask;
}
catch
{
Debug.WriteLine(tasksTask.Exception.Message);
}
}
这可能与 another issue I have had 有关 - 我使用的是旧版本的 Xamarin.Forms,它的 OnAppearing 正确处理异步存在问题。我将尝试使用更新的版本,看看它是否能解决问题。
【问题讨论】:
-
ExhibitStrangeBehaviorAsync的运行情况如何?这是控制台还是 WPF/WinForms 应用程序? -
在控制台中对我来说很好。这段代码用在哪里?
-
那里没有应该挂起的东西,我也无法在控制台应用程序中重现它。我猜你正在使用
.Wait()更进一步,这是一个 GUI 应用程序?但是一个 complete 复制器会很好,所以我们不是在猜测。如果你打电话给ExhibitStrangeBehaviorAsync().Wait(),有一场比赛可能会导致死锁
标签: c# xamarin asynchronous exception xamarin.forms