【问题标题】:C# .NET async await return typeC# .NET 异步等待返回类型
【发布时间】:2016-10-14 23:41:32
【问题描述】:

只是希望有人可以在 C# 中使用 asyncawait 时清除返回类型。我了解您有三种返回类型,分别是 Task<T>Taskvoid

通过尝试了解这一点,我已阅读您应该只在特殊情况下使用 void(事件处理程序),如果您将同步方法更改为 async 方法,那么您应该将 void 关键字更改为Task.

我的问题是,即使代码完全独立并且结果与继续无关,我是否需要 Task 返回类型?例如,我使用异步方法将一些信息推送到 Web 服务。

我创建了一个小例子来帮助解释:

private void CallingMethod()
{
    AsyncMethod();
    AsyncTaskMethod(); // any difference between doing these? (not exc handling)

    var task = AsyncTaskMethod(); // should I do anything with this task
                                  // before I continue? Or is it simply 
                                  // just to show the user that it's async??

    // continue with unrelated code that doesnt require task or any code from above
}

private async void AsyncMethod() // is void okay?
{
    await LongRunningMethod();

    // do other stuff
}

private async Task AsyncTaskMethod() // or should it be Task?
{
    await LongRunningMethod();

    // do other stuff
}

private async Task LongRunningMethod()
{
    // do long running code
}

我的应用程序有很多这样的方法:

private void BigMethod()
{
    DoStuff();

    DoMoreStuff();

    AsyncMethod(); // is this right? Or should I have the task declaration too?

    UnrelatedStuff();
}

private async Task AsyncMethod() // should this return task?
{
    var result = await GetUserId();

    if (result == null)
        return;

    MyUserId = result;
}

private async Task<int> GetUserId()
{
    // do long running code
    return 1;
}

如果不清楚,请直接说,我会尝试整理一下。谢谢大家。

编辑:

private void CallingMethod()
{
    AsyncMethod();
}

private async void AsyncMethod()
{
    await LongRunningMethod();
}

private async Task LongRunningMethod()
{
    await Task.Run(() =>
    {
        Thread.Sleep(4000);
    });

    MessageBox.Show("Done!");
}

针对第一条评论,上面的CallingMethod()没有使用async/await,它愉快的休眠了4秒,没有锁住UI线程,然后弹出消息框。有什么问题?

【问题讨论】:

  • CallingMethod() 应该使用asyncawait。没有这个问题就没有意义了。
  • 请注意,“async”与“parellel”不同:“await”行之后的代码在该行等待完成之前不会执行。
  • 不需要吗?请检查编辑
  • @Murphybro2:你不应该有一堆“一劳永逸”式的方法;如果这样做,则说明设计有问题。使用同步代码,您只需直接调用F();对于异步代码,通常的模式是调用await FAsync()。如果您发现自己需要在没有await 的情况下做很多FAsync(),这表明您正在尝试做的事情可能有问题。 (而且这个问题没有足够的上下文来诊断这个设计级问题)。
  • 嗨@StephenCleary,感谢您的回复。我在写这个问题时有点困惑,并且没有正确复制我的应用程序如何使用 async/await!由于某种原因,我将直接在它们前面的 await 的异步调用误认为是“一劳永逸”!我会为那个举手..

标签: c# .net asynchronous return async-await


【解决方案1】:

当方法返回 void 时,您不必必须使用 Task,但它确实是首选,原因如下:

  1. 返回Task(或Task&lt;T&gt;)的异步方法会将它们的异常放在返回的Task 上,因此调用方法可以检查(当它们认为合适时)Task 是否出错或不是。

  2. Async void 方法的异常直接在调用同步上下文中抛出,这几乎(几乎,因为您仍然可以使用全局异常处理程序,但这对于大型应用程序来说是不可维护的)不可能以确定的方式处理它们的异常。

  3. 你基本上不能测试任何可能抛出异常的async void方法

  4. async void 方法在调用者运行时被认为是完整的。这会引入很多问题(事实上,当您尝试将现有库转换为异步时,事件处理程序存在很多问题:继续,获取第三方相对复杂的 winforms 库并尝试使您的事件处理程序异步,您会看到它造成的破坏!)。

  5. 没有合理的方法来确定 async void 方法何时完成(您可以创建自己的 SynchronizationContext,但这肯定很复杂)

  6. 你不能配置async void调用(你不能在上面使用ConfigureAwait),所以延续上下文总是调用者的同步上下文。

所以……

TL;DR: 不要使用async void,除非你无法避免(即:使用需要void 签名的事件处理程序)。这是有原因的。如果您不期望结果,请使用async Task

【讨论】:

  • 好的,所以我有这个代码: public void TryToCatchMethod() { try { MyAsyncMethod(); } catch (Exception exception) { //tha catch 永远不会被调用 Debug.WriteLine(exception.ToString()); } } public async void MyAsyncMethod() { throw new Exception(); } 当抛出异常时应用程序崩溃,未处理的异常。那么我该如何改变它来捕获异常呢?
  • 我为格式道歉,请参阅 Radin Gospodinovs 对格式化版本的回答
  • 使异步方法返回Task,并让调用者方法await它:check it here。让它一直异步(当我们这样做时,将调用者方法更改为也返回Task
  • 好的,我明白了。所以每当我使用异步方法时,我也应该在它异步之前进行每个调用?
  • @Murphybro2 我将实现我的ICommand 并让Execute 方法(async void 出于必要)调用我自己的异步方法返回Task。你甚至可以有你的interface IAsyncCommand : ICommand 并在上面有一些Task ExecuteAsync() 签名。 void Execute 方法只是一个占位符(至少我是这样实现的)
【解决方案2】:

您不能等待返回 void 的方法,主要原因之一是返回 void 的异步方法的调用者无法捕获从异步方法抛出的异常。试试看:

  public void TryToCatchMethod() {
    try {
      MyAsyncMethod();
    } catch (Exception exception) {
      //tha catch is never called
      Debug.WriteLine(exception.ToString());
    }
  }
  public async void MyAsyncMethod() {
    throw new Exception();
  }

【讨论】:

  • 好的,谢谢,太好了。那么有什么选择呢?用任务替换无效?这样做会阻止程序崩溃的异常,但它似乎也没有被捕获?
  • 用这个替换它: public async Task MyAsyncMethod() { throw new Exception(); } 然后等待它,你会看到 catch 块是如何被执行的。
猜你喜欢
  • 2023-03-24
  • 2020-03-13
  • 2021-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-01
  • 2014-10-01
  • 1970-01-01
相关资源
最近更新 更多