抑制“警告 CS4014:因为不等待此调用,当前方法的执行继续......”
Posted
技术标签:
【中文标题】抑制“警告 CS4014:因为不等待此调用,当前方法的执行继续......”【英文标题】:Suppressing "warning CS4014: Because this call is not awaited, execution of the current method continues..." 【发布时间】:2014-05-03 00:32:38 【问题描述】:这不是 "How to safely call an async method in C# without await" 的副本。
如何很好地抑制以下警告?
警告 CS4014:由于未等待此调用,因此在调用完成之前继续执行当前方法。考虑将 'await' 运算符应用于调用结果。
一个简单的例子:
static async Task WorkAsync()
await Task.Delay(1000);
Console.WriteLine("Done!");
static async Task StartWorkAsync()
WorkAsync(); // I want fire-and-forget
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
我尝试过的和不喜欢的:
static async Task StartWorkAsync()
#pragma warning disable 4014
WorkAsync(); // I want fire-and-forget here
#pragma warning restore 4014
// ...
static async Task StartWorkAsync()
var ignoreMe = WorkAsync(); // I want fire-and-forget here
// ...
更新,由于original accepted answer 已被编辑,我已将接受的答案更改为the one using C# 7.0 discards,因为我认为ContinueWith
在这里不合适。每当我需要记录即发即弃操作的异常时,我会使用更精细的方法proposed by Stephen Cleary here。
【问题讨论】:
那么,你觉得#pragma
不好?
当您实际上并没有做任何异步操作时,为什么要让您的方法异步?
@FrédéricHamidi,我愿意。
@Noseratio:啊,对。抱歉,我还以为是另一个警告。无视我!
@Terribad:我不太确定——对于大多数情况,警告似乎是相当合理的。特别是,您应该考虑您希望对任何失败发生什么 - 通常即使是“一劳永逸”,您也应该弄清楚如何记录失败等。
【参考方案1】:
使用 C# 7,您现在可以使用 discards:
_ = WorkAsync();
【讨论】:
这是一个方便的小语言功能,我只是不记得了。就像我的大脑里有一个_ = ...
。
我发现一个 SupressMessage 从我的 Visual Studio“错误列表”中删除了我的警告,但没有从“输出”中删除,#pragma warning disable CSxxxx
看起来比丢弃更难看;)【参考方案2】:
您可以创建一个可以防止警告的扩展方法。扩展方法可以为空,也可以在此处使用.ContinueWith()
添加异常处理。
static class TaskExtensions
public static void Forget(this Task task)
task.ContinueWith(
t => WriteLog(t.Exception); ,
TaskContinuationOptions.OnlyOnFaulted);
public async Task StartWorkAsync()
this.WorkAsync().Forget();
但是 ASP.NET 会计算正在运行的任务数,因此它不适用于上面列出的简单 Forget()
扩展,而是可能会失败并出现以下异常:
异步模块或处理程序已完成,而异步操作仍处于挂起状态。
使用 .NET 4.5.2 可以通过使用 HostingEnvironment.QueueBackgroundWorkItem
来解决:
public static Task HandleFault(this Task task, CancellationToken cancelToken)
return task.ContinueWith(
t => WriteLog(t.Exception); ,
cancelToken,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Default);
public async Task StartWorkAsync()
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
cancelToken => this.WorkAsync().HandleFault(cancelToken));
【讨论】:
我找到了TplExtensions.Forget
。 Microsoft.VisualStudio.Threading
下还有很多优点。我希望它可以在 Visual Studio SDK 之外使用。
@Noseratio 和 Knagis,我喜欢这种方法并且我打算使用它。我贴了一个相关的后续问题:***.com/questions/22864367/fire-and-forget-approach
@stricq 将 ConfigureAwait(false) 添加到 Forget() 的目的是什么?据我了解,ConfigureAwait 只在 Task 上使用 await 时影响线程同步,但 Forget() 的目的是丢弃 Task,因此永远无法等待 Task,所以这里的 ConfigureAwait 毫无意义。
如果生成线程在 fire and forget 任务完成之前就消失了,如果没有 ConfigureAwait(false),它仍然会尝试将自己编组回生成线程,该线程已经消失,因此死锁。设置 ConfigureAwait(false) 告诉系统不要编组回调用线程。
这个回复有一个编辑来管理一个特定的案例,加上一打 cmets。简单的事情往往是正确的事情,去丢弃!我引用@fjch1997 的回答:创建一个需要更多时间来执行的方法是愚蠢的,只是为了抑制警告。【参考方案3】:
我的两种处理方式。
将其保存到丢弃变量 (C# 7)
例子
_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false);
自从在 C# 7 中引入了丢弃,我现在认为这比抑制警告更好。因为它不仅抑制了警告,而且使即发即弃的意图清晰。
此外,编译器将能够在发布模式下对其进行优化。
压制它
#pragma warning disable 4014
...
#pragma warning restore 4014
是“一劳永逸”的一个很好的解决方案。
存在此警告的原因是因为在许多情况下,您不打算使用无需等待就返回任务的方法。当您确实打算开火并忘记时抑制警告是有道理的。
如果您不记得如何拼写 #pragma warning disable 4014
,只需让 Visual Studio 为您添加即可。按 Ctrl+。打开“快速操作”,然后“禁止 CS2014”
总而言之
仅仅为了抑制警告而创建一个需要更多时间来执行的方法是愚蠢的。
【讨论】:
这在 Visual Studio for Mac 7.0.1 (build 24) 中工作。 仅仅为了抑制警告而创建一个需要更多滴答声才能执行的方法是愚蠢的 - 这个根本不会添加额外的滴答声并且IMO 更具可读性:[MethodImpl(MethodImplOptions.AggressiveInlining)] void Forget(this Task @this) /* ... */ obj.WorkAsync().Forget();
@Noseratio 很多时候,当我使用AggressiveInlining
时,无论出于何种原因,编译器都会忽略它
我喜欢pragma选项,因为它超级简单,只适用于当前行(或部分)的代码,而不是整个方法。
不要忘记使用#pragma warning disable 4014
之类的错误代码,然后使用#pragma warning restore 4014
恢复警告。它在没有错误代码的情况下仍然有效,但如果您不添加错误编号,它将抑制所有消息。【参考方案4】:
您可以使用以下属性来装饰方法:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
WorkAsync();
// ...
基本上你是在告诉编译器你知道你在做什么,它不需要担心可能的错误。
这段代码的重要部分是第二个参数。 “CS4014:”部分是抑制警告的原因。你可以在其余部分写任何你想写的东西。
【讨论】:
不适用于我:Visual Studio for Mac 7.0.1(内部版本 24)。似乎应该但是——不。[SuppressMessage("Compiler", "CS4014")]
抑制错误列表窗口中的消息,但输出窗口仍然显示警告行【参考方案5】:
停止警告的一种简单方法是在调用任务时简单地分配任务:
Task fireAndForget = WorkAsync(); // No warning now
所以在你原来的帖子中你会这样做:
static async Task StartWorkAsync()
// Fire and forget
var fireAndForget = WorkAsync(); // Tell the compiler you know it's a task that's being returned
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
【讨论】:
我在问题本身中提到了这种方法,作为我不特别喜欢的方法之一。 哎呀!没有注意到,因为它与您的 pragma one 在同一代码部分中......我正在寻找答案。除此之外,您不喜欢这种方法的什么地方? 我不喜欢task
看起来像一个被遗忘的局部变量。几乎就像编译器应该给我另一个警告一样,诸如“task
已分配但它的值从未使用过”之类的东西,除了它没有。此外,它使代码的可读性降低。我自己使用this 方法。
很公平 - 我有类似的感觉,这就是为什么我将它命名为 fireAndForget
...所以我希望它从此不再被引用。【参考方案6】:
警告的原因是 WorkAsync 返回一个从未读取或等待的 Task
。您可以将 WorkAsync 的返回类型设置为 void
,警告将消失。
当调用者需要知道worker的状态时,一个方法通常会返回一个Task
。在触发后不管的情况下,应该返回 void 以使调用者独立于被调用的方法。
static async void WorkAsync()
await Task.Delay(1000);
Console.WriteLine("Done!");
static async Task StartWorkAsync()
WorkAsync(); // no warning since return type is void
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
【讨论】:
【参考方案7】:为什么不将它包装在返回 void 的异步方法中?有点长,但是所有变量都用到了。
static async Task StartWorkAsync()
async void WorkAndForgetAsync() => await WorkAsync();
WorkAndForgetAsync(); // no warning
【讨论】:
【参考方案8】:我今天偶然发现了这种方法。您可以先定义一个委托,然后将异步方法分配给该委托。
delegate Task IntermediateHandler();
static async Task AsyncOperation()
await Task.Yield();
然后这样称呼它
(new IntermediateHandler(AsyncOperation))();
...
我觉得有趣的是,编译器在使用委托时不会给出完全相同的警告。
【讨论】:
不需要声明委托,你也可以做(new Func<Task>(AsyncOperation))()
,虽然IMO还是有点太冗长了。以上是关于抑制“警告 CS4014:因为不等待此调用,当前方法的执行继续......”的主要内容,如果未能解决你的问题,请参考以下文章
R语言ggplot2可视化抑制可视化网格中的竖线输出抑制可视化网格中的横线线输出抑制背景网格输出实战