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

C# 调用异步方法时使用.Wait()的情况是什么,c#,asynchronous,asp.net-mvc-5,async-await,entity-framework-6,C#,Asynchronous,Asp.net Mvc 5,Async Await,Entity Framework 6,在asp.net mvc-5 web应用程序中,我有以下async长时间运行的方法:- public async Task<ScanResult> ScanAsync(string FQDN) { // sample of the operation i am doing var c = await context.SingleOrDefaultAsync(a=>a.id == 1); var list = await context.Employ

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

 public async Task<ScanResult> 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调度程序调用同步方法,如下所示:-

public void Scan()
{
    ScanAsync("test").Wait();
}
RecurringJob.AddOrUpdate(() => ss.Scan(), Cron.Minutely);
因此,我知道在方法执行期间,使用
.Wait()
将主要占用iis线程,但正如我提到的,我需要以这种方式执行,因为我无法直接调用hangefire调度程序中的异步任务

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

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

查询数据库的方法仍将异步执行,但您正在调用
Wait
这一事实意味着,即使您正在释放线程,当您停止它时,它也不会返回到ASP.NET线程池

这也可能导致死锁,因为ASP.NET有一个自定义的同步上下文,可以确保在异步调用的继续中请求的上下文可用

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

编辑:

在评论中,你问:

正如我目前使用HangFire所做的那样,是否消除异步效果?如果是,那么使用同步方法会更好吗?或者与hangeffire使用同步或异步将完全相同

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


如果您实际上没有计划接收相当数量的请求(这样您将耗尽线程池),那么您实际上看不到异步的任何好处。目前,按照您实现它的方式,您不会看到任何这些好处,因为您正在阻止异步调用。你可能只会看到死锁。

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

考虑一下,如果你真的想等待你预定的工作完成。也许你想要的只是一堆火,忘记模式。这样,您的方法将类似于:

public void Scan()
{
    ScanAsync("test"); // smoothly ignore the task
}
如果确实需要等待,您可以尝试使用
async void
方法:

public async void Scan()
{
    await ScanAsync("test");
    DoSomeOtherJob();
}
关于使用
async void
有很多争议,因为您不能等待此方法结束,也不能处理可能的错误


然而,在事件驱动的应用程序中,这可能是唯一的方法。有关更多信息,您可以参考:

我不清楚(我认为John也不确定)的是,在Hangfire环境中,Hangfire本身将处理执行传递的lambda的问题有多重要。@Yuval您能进一步解释一下您的意思吗“我建议您改用entity framework提供的同步API,因为您不会真正享受异步调用所带来的可伸缩性。“??@mason Hangfire仍在IIS/ASP.NET下运行,并使用线程。如果Hangfire能够使用异步方法,它可以在等待数据库时将它们释放到池中。实际上,使用同步方法意味着编译器创建稍微简单的代码,因为如果代码仅由Hangfire使用,它不必生成实现
async/await
@johnG#3所需的代码。在Hangfire本身引入异步支持之前,使用异步代码没有任何好处#1如果控制器方法也调用了
ScanAsync
(可能不是一个好主意)#2将简单地破坏Hangfire及其仪表板。您将无法看到正在运行的作业,或者在必要时取消它们,因为作业将立即完成并留下孤立的任务running@johnG他们永远不会完全一样。至少,异步代码更复杂,因为编译器必须创建一个状态机,所以它的开销稍微大一些。这不是一个很大的数目,所以除非你真的需要资源,否则你可能不必担心。你不能这样称呼它吗<代码>RecurringJob.AddOrUpdate(async()=>wait ss.ScanAsync(),Cron.Minutely)我认为您需要做的一件事是检查任务,看看它是否有异常。类似这样:
var task=scanaxync(“测试”);task.Wait();如果(task.Exception!=null){throw task.Texception}如果不这样做,您可能无法发现后台作业中是否存在异常。@Igor否,Hangfire特别不支持直接执行异步方法。请参阅。@Igor它将引发一个错误,在本例中,Startupclass中的“RecurringJob.AddOrUpdate(async()=>wait ss.ScanAsync(),Cron。