在什么情况下会使用

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));
}

当前回答

通常,您会希望返回一个Task。主要的例外应该是当你需要一个void返回类型(用于事件)时。如果没有理由不允许调用者等待任务,为什么不允许呢? 返回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));
}

是的,在这里使用async和await,它们确保你的方法在抛出异常时仍然正确工作。

欲了解更多信息,请参阅:https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

其他回答

通常,您会希望返回一个Task。主要的例外应该是当你需要一个void返回类型(用于事件)时。如果没有理由不允许调用者等待任务,为什么不允许呢? 返回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));
}

是的,在这里使用async和await,它们确保你的方法在抛出异常时仍然正确工作。

欲了解更多信息,请参阅:https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

简要说明: async Task<T> method() await可用于等待执行完成,它将返回类型为T的值 async Task方法await可用于等待执行完成但不返回数据 async void方法()不能等待,没有数据返回[示例:async事件执行]

我偶然发现了Jérôme Laban写的关于async和void的非常有用的文章: https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

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

The reason behind this is the Synchronization Context used by the AsyncVoidMethodBuilder, being none in this example. When there is no ambient Synchronization Context, any exception that is unhandled by the body of an async void method is rethrown on the ThreadPool. While there is seemingly no other logical place where that kind of unhandled exception could be thrown, the unfortunate effect is that the process is being terminated, because unhandled exceptions on the ThreadPool effectively terminate the process since .NET 2.0. You may intercept all unhandled exception using the AppDomain.UnhandledException event, but there is no way to recover the process from this event. When writing UI event handlers, async void methods are somehow painless because exceptions are treated the same way found in non-async methods; they are thrown on the Dispatcher. There is a possibility to recover from such exceptions, with is more than correct for most cases. Outside of UI event handlers however, async void methods are somehow dangerous to use and may not that easy to find.

我认为你也可以使用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());
        }
    }
}

调用async void的问题是

你连任务都拿不回来。您无法知道函数的任务何时完成。——速成班在async和等待|旧的新事物

下面是调用async函数的三种方法:

async Task<T> SomethingAsync() { ... return t; } async Task SomethingAsync() { ... } async void SomethingAsync() { ... } In all the cases, the function is transformed into a chain of tasks. The difference is what the function returns. In the first case, the function returns a task that eventually produces the t. In the second case, the function returns a task which has no product, but you can still await on it to know when it has run to completion. The third case is the nasty one. The third case is like the second case, except that you don't even get the task back. You have no way of knowing when the function's task has completed. The async void case is a "fire and forget": You start the task chain, but you don't care about when it's finished. When the function returns, all you know is that everything up to the first await has executed. Everything after the first await will run at some unspecified point in the future that you have no access to.