13

我正在开发一个 Web API 项目,该项目使用 Azure 的托管缓存服务将数据库结果缓存在内存中,以缩短响应时间并减少数据库的重复流量。尝试将新项目放入缓存时,有时会抛出特定于缓存的异常,代码为DataCacheErrorCode.RetryLater. 自然地,为了稍后重试而不需要阻止这种方法,我做了它asyncawait Task.Delay稍后再试一次。以前,开发人员Thread.Sleep在那里硬编码了一个确实会损害应用程序性能的代码。

方法签名现在看起来与此类似:

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

Put在此更改之后,我从应用程序中调用以前同步版本的指示的所有其他位置收到约 75 个编译器警告:

由于不等待此调用,因此在调用完成之前继续执行当前方法。考虑将“等待”运算符应用于调用结果。

在这种情况下,由于Put不返回任何内容,因此让此操作一劳永逸对我来说是有意义,因为我看不出有任何理由阻止调用它的方法的执行。我只是想知道是否有任何危险或陷阱允许大量这些即发即弃Task的在后台运行,因为Put可以经常调用。或者我应该等待,因为 99% 的时间我不会收到重试错误,并且Task几乎会立即完成。我只是想确保我不会因为拥有太多线程(或类似的东西)而受到任何惩罚。

4

4 回答 4

9

如果有机会Put出于任何原因抛出任何其他异常,并且您await Put每次将对象插入缓存时都不会使用,则异常将被吞入返回Task的未等待的对象中。如果您使用的是 .NET 4.0,则此异常将在 .NET 的 Finalizer 中重新抛出Task.。如果您使用的是 .NET 4.5,它将被忽略(这可能是不可取的)。

想确保我不会因为线程太多或类似的东西而受到任何惩罚。

我这么说只是为了说明问题。当您使用 时Task.Delay,您不会旋转任何新线程。ATask并不总是等于正在旋转的新线程。特别是在这里,Task.Delay内部使用 a Timer,因此没有任何线程开销(如果您使用 ,则当前正在延迟的线程除外await)。

于 2014-11-17T17:36:18.157 回答
3

该警告告诉您,您在一个您可能实际上不想开火并忘记的位置发生了火灾并忘记了行为。如果您确实想触发并忘记并且在不知道操作何时完成或什至成功完成的情况下继续没有问题,那么您可以放心地忽略警告。

于 2014-11-17T17:31:03.250 回答
2

发布任务不等待运行的一个负面结果是编译器警告本身 - 75 个编译器警告本身就是一个问题,它们隐藏了真正的警告。

如果您想向编译器发出信号,表明您有意不对任务结果做任何事情,您可以使用一个简单的扩展方法,它什么都不做,但满足编译器对明确性的需求。

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

现在你可以打电话

UpdateCacheAsync(data).Release();

没有任何编译器警告。

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

于 2017-05-09T08:35:35.007 回答
0

推荐的 ASP.NET 方式是

HostingEnvironment.QueueBackgroundWorkItem(WorkItem);

...

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

顺便说一句,在 ASP.NET 线程之外的线程上不捕获/重新抛出可能导致服务器进程崩溃/重新启动

于 2017-05-09T08:20:21.327 回答