简体   繁体   English

不等待异步方法中的任务的注意事项

[英]Considerations for not awaiting a Task in an asynchronous method

I'm working on a Web API project which uses Azure's managed cache service to cache database results in memory to improve response times and alleviate duplicate traffic to the database. 我正在开发一个Web API项目,该项目使用Azure的托管缓存服务将数据库结果缓存到内存中,以缩短响应时间并减轻数据库的重复流量。 When attempting to put a new item in the cache, occasionally a cache-specific exception will be thrown with a code of DataCacheErrorCode.RetryLater . 尝试在缓存中放入新项时,有时会使用DataCacheErrorCode.RetryLater代码抛出特定于缓存的异常。 Naturally, in order to retry later without needing to block on this method I made it async and await Task.Delay to try again a short time later. 当然,为了稍后重试而不需要阻止此方法,我将其await Task.Delay asyncawait Task.Delay稍后再次尝试。 Previously a developer had hardcoded a Thread.Sleep in there that was really hurting application performance. 以前,开发人员在那里硬编码了Thread.Sleep ,这确实损害了应用程序的性能。

The method signature looks something similar to this now: 方法签名看起来与此类似:

public static async Task Put(string cacheKey, object obj)

Following this change I get ~75 compiler warnings from all the other places in the application that called the formerly synchronous version of Put indicating: 在此更改之后,我从应用程序中调用以前同步版本Put所有其他位置获得~75个编译器警告:

Because this call is not awaited, execution of the current method continues before the call is completed. 因为不等待该调用,所以在调用完成之前继续执行当前方法。 Consider applying the 'await' operator to the result of the call. 考虑将'await'运算符应用于调用的结果。

In this case, since Put doesn't return anything, it makes sense to me to let this operation fire-and-forget as I don't see any reason to block execution of the method that called it. 在这种情况下,由于Put没有返回任何内容,因此我有理由让这个操作一劳永逸,因为我没有看到任何理由阻止执行调用它的方法。 I'm just wondering if there are any dangers or pitfalls for allowing a lot of these fire-and-forget Task s running in the background as Put can be called quite often. 我只是想知道是否有任何危险或陷阱允许在后台运行很多这些即发即弃的Task ,因为可以经常调用Put Or should I await anyway since 99% of the time I won't get the retry error and the Task will finish almost immediately. 或者我应该等待,因为99%的时间我不会得到重试错误, Task将几乎立即完成。 I just want to make sure that I'm not incurring any penalties for having too many threads (or something like that). 我只是想确保我没有因为有太多线程(或类似的东西)而受到任何处罚。

If there is a chance Put will throw any other exception for any kind of reason, and you don't use await Put each time you're inserting an object to the cache, the exceptions will be swallowed inside the returned Task which isn't being awaited. 如果Put有可能因为任何原因抛出任何其他异常,并且每次将对象插入缓存时都不使用await Put ,则异常将被返回到返回的Task ,而不是等待着。 If you're on .NET 4.0, this exception will be re-thrown inside the Finalizer of that Task. 如果您使用的是.NET 4.0,则会在该Task.的Finalizer中重新抛出此异常Task. . If you're using .NET 4.5, it will simply be ignored (and that might not be desirable). 如果您使用的是.NET 4.5,它将被忽略(这可能并不理想)。

Want to make sure that I'm not incurring any penalties for having too many threads or something like that. 想要确保我没有因为有太多线程或类似的东西而受到任何处罚。

Im just saying this to make things clear. 我只是这样说清楚。 When you use Task.Delay , you aren't spinning any new threads. 当您使用Task.Delay ,您不会旋转任何新线程。 A Task isn't always equal to a new thread being spun. Task并不总是等于正在旋转的新线程。 Specifically here, Task.Delay internally uses a Timer , so there aren't any thread overheads (except for the thread which is currently being delayed if you do use await ). 具体来说, Task.Delay内部使用一个Timer ,因此没有任何线程开销(如果你使用await ,当前被延迟的线程除外)。

The warning is telling you that you're getting fire and forget behavior in a location where you may not actually want to fire and forget. 警告告诉你,在你可能实际上不想开火和忘记的地方,你会着火并忘记行为。 If you really do want to fire and forget and don't have a problem continuing on without ever knowing when the operation finishes, or if it even finished successfully, then you can safely ignore the warning. 如果你确实想要解雇并忘记并且在没有知道操作何时结束或者甚至成功完成的情况下仍然没有问题,那么你可以放心地忽略警告。

One negative result of releasing a task to run unawaited is the compiler warnings themselves - 75 compiler warnings are a problem in and of themselves and they hide real warnings. 释放任务以使其无法运行的一个负面结果是编译器警告本身 - 编译器警告本身就是一个问题,它们隐藏了真正的警告。

If you want to signal the compiler that you're intentionally not doing anything with the result of the task, you can use a simple extension method that does nothing, but satisfies the compiler's desire for explicitness. 如果您想要通知编译器您有意不对任务的结果做任何事情,您可以使用一个不做任何事情的简单扩展方法,但满足编译器对显式性的要求。

// Do nothing.
public static void Release(this Task task)
{
}

Now you can call 现在你可以打电话了

UpdateCacheAsync(data).Release();

without any compiler warnings. 没有任何编译器警告。

https://gist.github.com/lisardggY/396aaca7b70da1bbc4d1640e262e990a https://gist.github.com/lisardggY/396aaca7b70da1bbc4d1640e262e990a

Recommended ASP.NET way is 推荐的ASP.NET方式是

HostingEnvironment.QueueBackgroundWorkItem(WorkItem);

...

async Task WorkItem(CancellationToken cancellationToken)
{
    try { await ...} catch (Exception e) { ... }
}

BTW Not catching/re-throw on thread other then ASP.NET thread can cause server process to be crashed/restarted BTW没有捕获/重新抛出线程,然后ASP.NET线程可能导致服务器进程崩溃/重新启动

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM