提问人:user981225 提问时间:8/27/2012 最后编辑:Boiethiosuser981225 更新时间:1/12/2023 访问量:561779
async/await - 何时返回任务与无效?
async/await - when to return a Task vs void?
问:
在什么情况下要使用
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
await
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));
}
是的,使用,在这里,他们确保在抛出异常时您的方法仍然正常工作。async
await
评论
来自 f
的异常将传递给 . 将引发 ,但如果未处理,则不再使进程崩溃。在某些情况下,忽略这样的“异步异常”是可以接受的。f
g
SynchronizationContext
g
UnobservedTaskException
UTE
WhenAny
Task
WhenAny
我遇到了这篇非常有用的文章,内容是关于杰罗姆·拉班(Jérôme Laban)撰写的:https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.htmlasync
void
底线是 可能会使系统崩溃,通常只应在 UI 端事件处理程序上使用。async+void
这背后的原因是 AsyncVoidMethodBuilder,在此示例中为 none。当没有 环境同步上下文,任何未由 异步 void 方法的主体在 ThreadPool 上重新抛出。而 似乎没有其他合乎逻辑的地方可以处理这种未经处理的地方 可能会抛出异常,不幸的影响是该过程 正在终止,因为 ThreadPool 上出现未经处理的异常 自 .NET 2.0 以来有效地终止进程。您可以拦截 使用 AppDomain.UnhandledException 事件的所有未处理的异常, 但是无法从此事件中恢复该过程。
在编写 UI 事件处理程序时,异步 void 方法在某种程度上是 无痛,因为异常的处理方式与 非异步方法;它们被抛出到 Dispatcher 上。有一个 从此类异常中恢复的可能性,非常正确 在大多数情况下。但是,在 UI 事件处理程序之外,async void 方法使用起来很危险,而且可能不那么容易找到。
评论
我认为您也可以使用它来启动后台操作,只要您小心捕获异常即可。思潮?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 Task<T> SomethingAsync() { ... return t; } async Task SomethingAsync() { ... } async void SomethingAsync() { ... }
在所有情况下,函数都会转换为任务链。区别在于函数返回的内容。
在第一种情况下,该函数返回一个最终生成 t 的任务。
在第二种情况下,该函数返回一个没有产品的任务,但您可以 仍然在等待它,知道它何时完成。
第三种情况是令人讨厌的情况。第三种情况与第二种情况类似,只是 你甚至没有把任务拿回来。你无法知道什么时候 函数的任务已完成。
异步 void 情况是“火灾和 forget“:你启动了任务链,但你不关心它什么时候启动 完成。当函数返回时,您所知道的就是一切 直到第一个等待已执行。第一次之后的一切都在等待 将在将来某个未指定的时间点运行,而您没有 访问。
我的答案很简单 你不能等待 void 方法
Error CS4008 Cannot await 'void' TestAsync e:\test\TestAsync\TestAsyncProgram.cs
所以如果方法是,最好是等待,因为你可能会失去优势。async
async
评论
简单说明:
await 可用于等待执行完成,其类型
为 await 可用于等待执行完成,但不返回任何数据 can't be awawait,不返回
任何数据 [示例:异步事件执行]async Task<T> method()
return value
T
async Task method()
async void method()
评论
Thread.Sleep
await Task.Delay(num)
Task.Delay
Task.AsyncDelay
async void
async Task