我的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工具及时调用此异步方法,但不幸的是,hangefire工具不支持直接调用异步方法。所以为了克服这个问题,我创建了上述方法的同步版本,如下所示: -
public void Scan()
{
ScanAsync("test").Wait();
}
然后从HangFire调度程序我调用sync方法如下: -
RecurringJob.AddOrUpdate(() => ss.Scan(), Cron.Minutely);
所以我知道在方法执行期间使用.Wait()
将主要占用iis线程,但正如我所提到的,我需要这样做,因为我无法在hangefire调度程序中直接调用异步TASK
那么当我使用.Wait()
调用异步方法时会发生什么?,整个方法的操作是否会以同步方式完成?例如,如上所示,我在ScanAsync()
; SingleOrDefualtAsync
,ToListAsync
&amp;内部有三个异步操作。 SaveChangesAsync
,所以它们会以同步的方式执行,因为我使用.wait()调用ScanAsync方法吗?
答案 0 :(得分:3)
那么当我使用.Wait()调用异步方法时会发生什么? 整个方法的操作是否会以同步方式完成?例如 如上所示,我在ScanAsync()内部有三个异步操作 ; SingleOrDefualtAsync,ToListAsync&amp; SaveChangesAsync,他们也是 以同步方式执行,因为我正在调用ScanAsync方法 .wait()?
查询数据库的方法仍然是异步执行的,但是你调用Wait
的事实意味着即使你正在发布该线程,它也不会像你那样返回到ASP.NET ThreadPool。重新制止它。
这也是一种死锁的可能性,因为ASP.NET有一个自定义同步上下文,可确保在继续执行异步调用时可以使用请求的上下文。
我建议您使用entity-framework提供的同步API,因为您实际上不会享受异步调用可以获得的可伸缩性。
修改强>
在评论中,你问:
正如我目前正在使用hangefire消除异步效应?如果是,那么使用同步方法会更好吗?或者使用hangeffire的同步或异步将完全相同
首先,您必须了解异步的好处是什么。你不这样做是因为它很酷,你这样做是因为它有用。那个目的是什么?能够在负载下扩展。它是如何做到的?当您await
异步方法时,控制权将返回给调用者。例如,您有一个传入请求,您查询数据库。您可以坐在那里等待查询完成,或者您可以重新使用该线程来提供更多不一致的请求。这才是真正的力量。
如果你实际上没有计划接收大量的请求(这样你就会让线程池挨饿),你实际上看不到异步的任何好处。目前,您实施它的方式,您将看不到任何这些好处,因为您阻止了异步调用。所有你可能会看到的都是死锁。
答案 1 :(得分:0)
这在很大程度上取决于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, and Count of Outstanding Operations