调用异步方法时使用.Wait()的情况是什么

我的asp.net mvc-5 Web应用程序中有以下async长时间运行方法: –

  public async Task ScanAsync(string FQDN) { // sample of the operation i am doing var c = await context.SingleOrDefaultAsync(a=>a.id == 1); var list = await context.Employees.ToListAsync(); await context.SaveChangesAsync(); //etc.. } 

我正在使用支持运行后台作业的Hangfire工具来及时调用此异步方法,但不幸的是,hangefire工具不支持直接调用异步方法。 所以为了克服这个问题,我创建了上述方法的同步版本,如下: –

 public void Scan() { ScanAsync("test").Wait(); } 

然后从HangFire调度程序我调用sync方法如下: –

 RecurringJob.AddOrUpdate(() => ss.Scan(), Cron.Minutely); 

所以我知道使用.Wait()将主要在方法执行期间占用iis线程,但正如我所提到的,我需要这样做,因为我无法在hangefire调度程序中直接调用异步TASK。

那么当我使用.Wait()调用异步方法时会发生什么?整个方法的操作是否会以同步方式完成? 例如,如上所示,我在ScanAsync()有三个异步操作; SingleOrDefualtAsyncToListAsyncSaveChangesAsync ,它们将以同步方式执行,因为我使用.wait()调用ScanAsync方法?

那么当我使用.Wait()调用异步方法时会发生什么?整个方法的操作是否会以同步方式完成? 例如,如上所示,我在ScanAsync(); SingleOrDefualtAsync,ToListAsync和SaveChangesAsync中有三个异步操作,因此它们将以同步方式执行,因为我使用.wait()调用ScanAsync方法?

查询数据库的方法仍然是异步执行的,但是你调用Wait的事实意味着即使你正在释放该线程,它也不会在你停止它时返回到ASP.NET ThreadPool。

这也是一种死锁的可能性,因为ASP.NET有一个自定义同步上下文,可确保在继续异步调用时可以使用请求的上下文。

我建议您使用entity framework提供的同步API,因为您实际上不会享受异步调用可以获得的可伸缩性。

编辑:

在评论中,你问:

正如我目前正在使用hangefire消除异步效应? 如果是,那么使用同步方法会更好吗? 或者使用hangeffire的同步或异步将完全相同

首先,您必须了解异步的好处是什么。 你不这样做因为它很酷,你这样做是因为它有用。 那个目的是什么? 能够在负载下扩展 。 它是如何做到的? 当您await异步方法时,控制权将返回给调用者。 例如,您有一个传入请求,您查询数据库。 您可以坐在那里等待查询完成,或者您可以重新使用该线程来提供更多不一致的请求。 这才是真正的力量。

如果你实际上没有计划接收大量的请求(这样你就会使线程池挨饿),你实际上看不到异步的任何好处。 目前,您实施它的方式,您将看不到任何这些好处,因为您阻止了异步调用。 所有你会看到,可能是死锁。

这在很大程度上取决于HangFire的实施方式。 如果它只是在ThreadPool调用要排队的任务,那么唯一的结果就是,在请求结束之前,你的一个线程将被阻塞。 但是,如果存在自定义SynchronizationContext则可能导致严重的死锁。

考虑一下,如果你真的想等待你的预定工作完成。 也许你想要的只是一个火与忘记模式。 这样你的方法就像:

 public void Scan() { ScanAsync("test"); // smoothly ignore the task } 

如果确实需要等待,可以尝试使用async void方法:

 public async void Scan() { await ScanAsync("test"); DoSomeOtherJob(); } 

关于使用async void有很多争议,因为你不能等待这个方法结束,也不能处理可能的错误。

但是,在事件驱动的应用程序中,这可能是唯一的方法。 有关更多信息,您可以参考: Async Void,ASP.Net和Outstanding Operations计数