C# 压制;警告CS4014:由于未等待此调用,因此当前方法的执行将继续…“;
这不是一个复制品 如何很好地抑制以下警告? 警告CS4014:由于未等待此调用,因此当前方法的执行将在调用完成之前继续。考虑将“Acess”运算符应用到调用的结果。< /P> 一个简单的例子:C# 压制;警告CS4014:由于未等待此调用,因此当前方法的执行将继续…“;,c#,async-await,C#,Async Await,这不是一个复制品 如何很好地抑制以下警告? 警告CS4014:由于未等待此调用,因此当前方法的执行将在调用完成之前继续。考虑将“Acess”运算符应用到调用的结果。< /P> 一个简单的例子: static async Task WorkAsync() { await Task.Delay(1000); Console.WriteLine("Done!"); } static async Task StartWorkAsync() { WorkAsync(); // I
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
// ...
}
已更新,由于已编辑,我已将接受的答案更改为,因为我认为
继续使用
在此处不合适。每当我需要记录fire and forget操作的异常时,我都会使用一种更精细的方法。您可以创建一个扩展方法来防止警告。扩展方法可以为空,也可以使用.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,可以使用以下方法解决此问题:
我的两种处理方法 将其保存到丢弃变量(C#7) 范例
\=Task.Run(()=>domysuff()).ConfigureAwait(false)代码>
<>自从在C 7中引入丢弃,我现在认为这比压制警告要好。因为它不仅压制了警告,而且使火灾和遗忘的意图变得清晰
此外,编译器将能够在发布模式下对其进行优化
只需抑制它
#pragma warning disable 4014
...
#pragma warning restore 4014
这是一个很好的解决方案,可以“开火然后忘记”
出现此警告的原因是,在许多情况下,您不打算使用不等待就返回任务的方法。当您确实打算开火并忘记时,抑制警告是有意义的
如果您记不起如何拼写#pragma warning disable 4014
,只需让Visual Studio为您添加它即可。按Ctrl+。打开“快速行动”然后“抑制CS2014”
总而言之
仅仅为了抑制警告而创建一个需要多点时间才能执行的方法是愚蠢的。您可以使用以下属性装饰该方法:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
WorkAsync();
// ...
}
基本上,你告诉编译器你知道你在做什么,它不需要担心可能的错误
此代码的重要部分是第二个参数。“CS4014:”部分是抑制警告的部分。您可以在其余部分写入任何内容。停止警告的一种简单方法是在调用时简单地分配任务:
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);
}
警告的原因是WorkAsync正在返回一个从未读取或等待的任务。您可以将WorkAsync的返回类型设置为void
,警告将消失
通常,当调用方需要知道工作人员的状态时,方法返回一个任务。在fire and forget的情况下,应该返回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);
}
使用C#7,您现在可以使用:
为什么不将它包装到一个返回void的异步方法中呢?有点长,但使用了所有变量
static async Task StartWorkAsync()
{
async void WorkAndForgetAsync() => await WorkAsync();
WorkAndForgetAsync(); // no warning
}
我今天偶然发现了这种方法。您可以定义一个委托,并首先将异步方法分配给该委托
delegate Task IntermediateHandler();
static async Task AsyncOperation()
{
await Task.Yield();
}
就这样说吧
(new IntermediateHandler(AsyncOperation))();
我觉得有趣的是,编译器在使用委托时不会给出完全相同的警告。那么,你认为#pragma
不好吗?为什么在你实际上没有做任何异步操作的情况下却让你的方法异步呢?@FrédéricHamidi,我做了。@noserratio:啊,对。对不起,我以为是另一个警告。别理我@特瑞巴德:我不太确定——看来这个警告在大多数情况下都是合理的。特别是,你应该考虑一下你希望在任何失败中发生什么——通常即使是“开火并忘记”你也应该知道如何记录失败等等,我发现了。下面有更多的善良。我希望它能在VisualStudioSDK之外使用,还有Knagis,我喜欢这种方法,并计划使用它。我发布了一个相关的后续问题:@stricq添加ConfigureAwait(false)以忘记()有什么作用?据我所知,ConfigureAwait仅在任务上使用await时影响线程同步,但Forget()的目的是丢弃任务,因此任务永远无法等待,因此ConfigureAwait在这里是毫无意义的。如果生成线程在fire and Forget任务完成之前消失,而没有ConfigureAwait(false)它仍将尝试将自己重新部署到生成线程上,该线程已消失,因此它会死锁。设置ConfigureWait(false)告诉系统不要将其返回到调用线程。此回复有一个用于管理特定案例的编辑,以及十几条注释。简单的事情往往是正确的事情,去丢弃!我引用@fjch1997的话回答:创建一个只需多打几次勾就可以执行的方法是愚蠢的,只是为了抑制一个警告。我在问题中提到了这种方法,作为我不太喜欢的方法之一。哇!没有注意到,因为它与您的pragma文件位于同一代码段中。。。我在寻找答案。除此之外,你不喜欢这个方法的什么地方?我不喜欢任务看起来像一个被遗忘的局部变量。就像编译器应该给我另一个警告一样,类似于“task
被分配了,但它的值从未被使用”,而且它没有。此外,它使代码的可读性降低
delegate Task IntermediateHandler();
static async Task AsyncOperation()
{
await Task.Yield();
}
(new IntermediateHandler(AsyncOperation))();