将async
与ASP.NET QueueBackgroundWorkItem
方法一起使用有什么好处?
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
var result = await LongRunningMethodAsync();
// etc.
});
我的理解是异步函数用于防止长时间运行的任务阻塞主线程。但是,在这种情况下,我们不是在自己的线程中执行任务吗?与非异步版本相比有什么优势:
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
var result = LongRunningMethod();
// etc.
});
答案 0 :(得分:13)
在ASP.NET QueueBackgroundWorkItem方法中使用async有什么好处?
它允许您的后台工作项调用异步API。
我的理解是异步函数用于防止长时间运行的任务阻塞主线程。但是,在这种情况下,我们还不在自己的线程中执行任务吗?
异步方法的优点是它们可以释放调用线程。没有"主线程"在ASP.NET上 - 只需要开箱即用的线程和线程池线程。在这种情况下,异步后台工作项将释放线程池线程,这可能会增加可伸缩性。
与非异步版本相比有什么优势
或者,您可以这样想:如果LongRunningOperationAsync
是一个自然异步的操作,那么LongRunningOperation
将阻止一个本来可以用于其他东西的线程。
答案 1 :(得分:4)
在ASP.NET QueueBackgroundWorkItem方法中使用async有什么好处?
简短回答
没有任何好处,实际上你不应该在这里使用async
!
答案很长
TL; DR
事实上,没有任何好处 - 在这种具体情况下,我实际上会反对它。来自MSDN:与普通的ThreadPool工作项不同,ASP.NET可以跟踪当前正在运行的通过此API注册的工作项数,并且ASP.NET运行时将尝试延迟AppDomain关闭,直到这些工作项完成执行。无法在ASP.NET管理的AppDomain之外调用此API。当应用程序关闭时,将提供所提供的CancellationToken信号。
QueueBackgroundWorkItem接受一个返回任务的回调;当回调返回时,工作项将被视为已完成。
这个解释松散地表明它是为你管理的。
根据“备注”,它可能需要Task
返回回调,但文档中的签名与之相冲突:
public static void QueueBackgroundWorkItem(
Action<CancellationToken> workItem
)
他们排除了文档的过载,这令人困惑和误导 - 但我离题了。微软的"Reference Source"来救援。这是两个重载的源代码以及scheduler
的内部调用,它完成了我们所关注的所有魔法。
旁注
如果你只想要排队的Action
,那就没问题,因为你可以看到他们只是在幕后使用完成的任务,但这似乎有点违反直觉。理想情况下,您实际上会有Func<CancellationToken, Task>
。
public static void QueueBackgroundWorkItem(
Action<CancellationToken> workItem) {
if (workItem == null) {
throw new ArgumentNullException("workItem");
}
QueueBackgroundWorkItem(ct => { workItem(ct); return _completedTask; });
}
public static void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem) {
if (workItem == null) {
throw new ArgumentNullException("workItem");
}
if (_theHostingEnvironment == null) {
throw new InvalidOperationException(); // can only be called within an ASP.NET AppDomain
}
_theHostingEnvironment.QueueBackgroundWorkItemInternal(workItem);
}
private void QueueBackgroundWorkItemInternal(
Func<CancellationToken, Task> workItem) {
Debug.Assert(workItem != null);
BackgroundWorkScheduler scheduler = Volatile.Read(ref _backgroundWorkScheduler);
// If the scheduler doesn't exist, lazily create it, but only allow one instance to ever be published to the backing field
if (scheduler == null) {
BackgroundWorkScheduler newlyCreatedScheduler = new BackgroundWorkScheduler(UnregisterObject, Misc.WriteUnhandledExceptionToEventLog);
scheduler = Interlocked.CompareExchange(ref _backgroundWorkScheduler, newlyCreatedScheduler, null) ?? newlyCreatedScheduler;
if (scheduler == newlyCreatedScheduler) {
RegisterObject(scheduler); // Only call RegisterObject if we just created the "winning" one
}
}
scheduler.ScheduleWorkItem(workItem);
}
最终,您最终得到scheduler.ScheduleWorkItem(workItem);
workItem
代表异步操作Func<CancellationToken, Task>
。可以找到here的来源。
正如您所看到的,SheduleWorkItem
仍然在workItem
变量中进行异步操作,然后实际调用ThreadPool.UnsafeQueueUserWorkItem
。这会调用使用RunWorkItemImpl
和async
的{{1}} - 因此您不需要处于最高级别,也不应该再次为您管理。
await
对内部here进行了更深入的阅读。
答案 2 :(得分:2)
关于标记答案的QueueBackgroundWorkItem的信息很好,但结论是错误的。
使用async with closure实际上将采用QueueBackgroundWorkItem( Func workItem)覆盖,它将等待任务完成,并在不保留任何线程的情况下完成。
所以对此的答案是,如果要使用async / await在workItem闭包中执行任何类型的IO操作。
答案 3 :(得分:-1)
让我区分您的示例为line 1
&amp;的4个代码段。 line 4
。
(我想我们中的一些人读到的问题是关于第1行与第3行;其他人关于第3行与第4行。我认为你的实际问题是1对4提出了两个问题。)
(– where HE.QueueBWI is actually HostingEnvironment.QueueBackgroundWorkItem() –)
HE.QueueBWI(async ct => { var result=await LongRunningMethodAsync(); /* etc */ });
HE.QueueBWI( ct => { var result= LongRunningMethodAsync().ContinueWith(t => {/*etc*/});});
HE.QueueBWI( ct => LongRunningMethodAsync() );
HE.QueueBWI( ct => LongRunningMethod() );
line 1
中async关键字的好处在于它允许您使用简单的await
语法,这种语法比line 2
更容易。但是,如果您没有使用结果,那么您不需要await
它,并且可以使用更具可读性的line 3
。
line 3
优于line 4
的优势如何?好吧,他们称之为完全不同的方法。 谁知道优势或劣势可能是什么?
当然,我们希望命名意味着它们真正实现了同样的目标,并且标记为...Async()
的方法异步地执行它并且(我们假设)以某种方式更有效。
在这种情况下,line 3
优于line 4
的优势在于它可能会使用或阻止更少的资源来完成工作。 但谁知道没有阅读代码?
我已经看到...Async()
方法明确声称在没有查看代码的情况下效率可能更高的一个示例FileStream.WriteAsync()
FileStream
已打开FileStream(..., FileOptions.IsASync)
{1}}。在这种情况下,FileOptions.IsAsync的说明建议您获得Overlapped I/O superpowers。 Stephen Cleary's blog表明,在没有进一步消耗线程资源的情况下,这可能会发生I / O.
这是我见过的唯一一个明确表示...Async()
方法更有效率的方法。也许还有更多。