615

在什么场景下想要使用

public async Task AsyncMethod(int num)

代替

public async void AsyncMethod(int num)

我能想到的唯一情况是您是否需要任务能够跟踪其进度。

另外,在下面的方法中,是否不需要 async 和 await 关键字?

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}
4

6 回答 6

499
  1. 通常,您会想要返回一个Task. 主要的例外应该是当您需要返回void类型(用于事件)时。如果没有理由不允许调用者await执行您的任务,为什么不允许它呢?

  2. async返回的方法void在另一个方面是特殊的:它们代表顶级异步操作,并且当您的任务返回异常时有额外的规则发挥作用。最简单的方法是通过一个示例来显示差异:

static async void f()
{
    await h();
}

static async Task g()
{
    await h();
}

static async Task h()
{
    throw new NotImplementedException();
}

private void button1_Click(object sender, EventArgs e)
{
    f();
}

private void button2_Click(object sender, EventArgs e)
{
    g();
}

private void button3_Click(object sender, EventArgs e)
{
    GC.Collect();
}

f的异常总是被“观察到”的。离开顶级异步方法的异常被简单地视为任何其他未处理的异常。g的异常从未被观察到。当垃圾收集器来清理任务时,它看到任务导致异常,没有人处理异常。发生这种情况时,TaskScheduler.UnobservedTaskException处理程序运行。你不应该让这种情况发生。要使用您的示例,

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}

是的,在这里使用asyncawait,它们确保您的方法在抛出异常时仍然可以正常工作。

有关详细信息,请参阅:https ://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

于 2012-08-27T14:53:45.313 回答
55

我看到了这篇关于 Jérôme Laban 并由 Jérôme Laban 撰写的非常有用的文章asyncvoidhttps: //jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void .html

底线是它async+void会导致系统崩溃,通常应该只在 UI 端事件处理程序上使用。

这背后的原因是 AsyncVoidMethodBuilder 使用的同步上下文,在这个例子中没有。当没有环境同步上下文时,任何由 async void 方法的主体未处理的异常都会在 ThreadPool 上重新引发。虽然似乎没有其他合乎逻辑的地方可以引发这种未处理的异常,但不幸的是进程正在被终止,因为 ThreadPool 上的未处理异常有效地终止了自 .NET 2.0 以来的进程。您可以使用 AppDomain.UnhandledException 事件拦截所有未处理的异常,但无法从该事件中恢复进程。

在编写 UI 事件处理程序时,异步 void 方法在某种程度上是无痛的,因为异常的处理方式与非异步方法中的处理方式相同;它们被扔到 Dispatcher 上。有可能从此类异常中恢复,对于大多数情况来说是正确的。然而,在 UI 事件处理程序之外,异步 void 方法使用起来有些危险,而且可能不容易找到。

于 2014-04-11T15:40:55.977 回答
43

调用 async void 的问题在于

你甚至没有拿回任务。您无法知道函数的任务何时完成。—— async 和 await 速成课程 | 旧事新事

以下是调用异步函数的三种方法:

async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }

在所有情况下,函数都被转换为一系列任务。不同之处在于函数返回的内容。

在第一种情况下,函数返回一个最终产生 t 的任务。

在第二种情况下,该函数返回一个没有产品的任务,但您仍然可以等待它以了解它何时运行完成。

第三种情况是令人讨厌的。第三种情况和第二种情况一样,只是你连任务都拿不回来。您无法知道函数的任务何时完成。

async void 情况是“一劳永逸”:您启动任务链,但您不关心它何时完成。当函数返回时,您所知道的就是直到第一次等待为止的所有内容都已执行。第一次等待之后的所有内容都将在未来某个您无法访问的未指定点运行。

于 2018-06-19T15:31:19.900 回答
9

我认为你也可以async void用来启动后台操作,只要你小心捕捉异常。想法?

class Program {

    static bool isFinished = false;

    static void Main(string[] args) {

        // Kick off the background operation and don't care about when it completes
        BackgroundWork();

        Console.WriteLine("Press enter when you're ready to stop the background operation.");
        Console.ReadLine();
        isFinished = true;
    }

    // Using async void to kickoff a background operation that nobody wants to be notified about when it completes.
    static async void BackgroundWork() {
        // It's important to catch exceptions so we don't crash the appliation.
        try {
            // This operation will end after ten interations or when the app closes. Whichever happens first.
            for (var count = 1; count <= 10 && !isFinished; count++) {
                await Task.Delay(1000);
                Console.WriteLine($"{count} seconds of work elapsed.");
            }
            Console.WriteLine("Background operation came to an end.");
        } catch (Exception x) {
            Console.WriteLine("Caught exception:");
            Console.WriteLine(x.ToString());
        }
    }
}
于 2018-02-16T12:43:42.293 回答
3

根据 Microsoft 文档,永远不要使用async void

不要这样做:以下示例使用async voidwhich 在到达第一个 await 时完成 HTTP 请求:

  • 这在 ASP.NET Core 应用程序中总是一个不好的做法。

  • 在 HTTP 请求完成后访问 HttpResponse。

  • 使进程崩溃。

于 2019-11-18T12:35:05.933 回答
-1

我的回答很简单,你不能等待 void 方法

Error   CS4008  Cannot await 'void' TestAsync   e:\test\TestAsync\TestAsyncProgram.cs

所以如果方法是async,最好是可等待的,因为你会失去 的优势async

于 2018-09-20T16:21:00.737 回答