use*_*225 457 c# asynchronous .net-4.5
在什么情况下会想要使用
public async Task AsyncMethod(int num)
Run Code Online (Sandbox Code Playgroud)
代替
public async void AsyncMethod(int num)
Run Code Online (Sandbox Code Playgroud)
我能想到的唯一情况是,您是否需要能够跟踪其进度的任务.
此外,在以下方法中,async和await关键字是否不必要?
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Run Code Online (Sandbox Code Playgroud)
小智 392
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();
}
Run Code Online (Sandbox Code Playgroud)
f
总是"观察"异常.离开顶级异步方法的异常被简单地视为任何其他未处理的异常.g
从来没有观察过例外.当垃圾收集器来清理任务时,它会发现该任务导致异常,并且没有人处理该异常.当发生这种情况时,TaskScheduler.UnobservedTaskException
处理程序运行.你永远不应该让这件事发生.要使用你的例子,
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Run Code Online (Sandbox Code Playgroud)
是的,使用async
和await
在这里,他们确保如果抛出异常,您的方法仍然可以正常工作.
有关更多信息,请参阅:http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Dav*_*rdi 38
我也碰到过这种关于非常有用的文章async
和void
杰罗姆拉班写着:
http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async -void.aspx
底线是async+void
可以使系统崩溃,通常只应在UI端事件处理程序上使用.
这背后的原因是AsyncVoidMethodBuilder使用的同步上下文,在此示例中为none.当没有环境同步上下文时,在ThreadPool上重新抛出异步void方法的主体未处理的任何异常.虽然似乎没有其他逻辑位置可以抛出那种未处理的异常,但不幸的结果是该进程被终止,因为ThreadPool上的未处理异常有效地终止了自.NET 2.0以来的进程.您可以使用AppDomain.UnhandledException事件拦截所有未处理的异常,但无法从此事件中恢复该进程.
在编写UI事件处理程序时,异步void方法在某种程度上是无痛的,因为异常的处理方式与非异步方法相同; 他们被扔在Dispatcher上.有可能从这些例外中恢复,对大多数情况来说都是正确的.但是,在UI事件处理程序之外,异步void方法在某种程度上是危险的,并且可能不容易找到.
Nay*_*ian 28
我从这些陈述中得到了明确的想法.
Async Void方法的例外情况无法通过Catch捕获
private async void ThrowExceptionAsync()
{
throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is never caught here!
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
对于GUI/ASP.NET应用程序,可以使用AppDomain.UnhandledException或类似的catch-all事件来观察这些异常,但是使用这些事件进行常规异常处理是不可维护性的一个因素(它会使应用程序崩溃).
异步void方法具有不同的组合语义.返回任务或任务的异步方法可以使用await,Task.WhenAny,Task.WhenAll等轻松编写.返回void的异步方法不提供通知调用代码已完成的简单方法.启动几个异步void方法很容易,但要确定它们何时完成并不容易.Async void方法将在启动和结束时通知其SynchronizationContext,但自定义SynchronizationContext是常规应用程序代码的复杂解决方案.
Async Void方法在使用同步事件处理程序时很有用,因为它们直接在SynchronizationContext上引发异常,这类似于同步事件处理程序的行为方式
有关更多详细信息,请访问此链接 https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
use*_*167 19
调用async void的问题是你甚至没有恢复任务,你无法知道函数的任务何时完成(参见https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/ ?p = 96655)
以下是调用异步函数的三种方法:
async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }
Run Code Online (Sandbox Code Playgroud)
在所有情况下,该功能都转换为一系列任务.不同之处在于函数返回的内容.
在第一种情况下,函数返回最终产生t的任务.
在第二种情况下,该函数返回一个没有产品的任务,但您仍然可以等待它知道它何时运行完成.
第三种情况是令人讨厌的.第三种情况就像第二种情况,除了你甚至没有得到任务.您无法知道函数的任务何时完成.
async void case是一个"一劳永逸":你启动任务链,但你不关心它什么时候结束.当函数返回时,你所知道的是第一个await的所有内容都已执行.第一次等待之后的所有内容将在未来的某个未指定的点运行,您无权访问.
我认为你也可以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());
}
}
}
Run Code Online (Sandbox Code Playgroud)