等待异步任务,而不在AggregateException中包装异常

时间:2013-07-16 03:18:35

标签: c# async-await

我使用的库提供以...Async结尾的方法并返回Task。我将在命令行应用程序中使用它们。所以我需要同步调用它们。

当然,C#不允许在Main方法中调用这些方法,因为您无法在async方法上使用Main修饰符。假设这是任务:

var task = datastore.Save(data);

我找到了几个解决方案,如:

Tasks.WaitAll(task);
task.Wait();

然而,AggregateException中所有这些包装抛出异常,我不希望。我只想说task.Result,我希望抛出原始异常。

当我使用返回Task<TResult>的方法时,task.Result会抛出AggregateException,即使没有设置连续任务。为什么这是hapening?

我也试过了,

task.RunSynchronously();

它给出错误:

  

可能不会在未绑定到委托的任务上调用RunSynchronously,例如从异步方法返回的任务。

所以我想这不是标记为async的方法。

在没有异步上下文的控制台应用程序中使用为异步应用程序设计的库的模式的任何想法?

3 个答案:

答案 0 :(得分:31)

  

我将在命令行应用程序中使用它们。所以我需要同步调用它们。

不,你没有。您可以在控制台应用程序中使用async - await,您只需要在最顶层进行异步同步转换。你可以使用Wait()

来做到这一点
public static void Main()
{
    MainAsync().Wait();
}

public static async Task MainAsync()
{
    var datastore = …;
    await datastore.SaveAsync();
}

通常情况下,将awaitWait()合并是一个坏主意(它可能会导致死锁),但这是正确的解决方案。

请注意,如果SaveAsync()引发异常并且您没有抓住它,则会从AggregateException重新引用Wait()。但您可以将其作为MainAsync()中的原始异常(因为它不使用Wait())来捕获它。

如果你真的想直接抛出第一个异常,你可以做类似于await做的事情:task.GetAwaiter().GetResult()。请注意,如果Task包含多个例外,则只会获得第一个例外(但同样适用于await)。

  

当我使用返回Task<TResult>的方法时,task.Result会抛出AggregateException,即使没有设置连续任务。为什么会这样?

这与延续无关。单个Task可以表示多个操作,每个操作都可以抛出异常。因此,Task方法总是抛出包含在AggregateException中的异常。

  

我也尝试了task.RunSynchronously()

这没有任何意义。 RunSynchronously()只能在使用Task构造函数创建的Task上使用。这不是这种情况,所以你不能使用它。从异步方法返回的Task始终已经启动。

答案 1 :(得分:2)

您可以创建一个虚拟Main

public static void Main()
{
    MainAsync().Wait();
}

public static async Task MainAsync()
{
    try {
        var result = await dataStore.Save(data);
    } catch(ExceptionYouWantToCatch e) {
       // handle it
    }
}

此外,请参阅此答案:https://stackoverflow.com/a/9212343/1529246

答案 2 :(得分:0)

As of C# 7.1 you can now declare the Main method as async. Just have to make sure your language is either set to default major version (which should work in VS 2019 now) or you can always target a specific version of the language.

See this link for details. Turn on async main