C# 警告:未等待此调用,将继续执行当前方法

C# 警告:未等待此调用,将继续执行当前方法,c#,async-await,C#,Async Await,刚刚获得VS2012并试图获得async的句柄 假设我有一个从阻塞源获取一些值的方法。我不希望该方法的调用方阻塞。我可以编写方法来接受回调,该回调在值到达时被调用,但由于我使用的是C#5,因此我决定将该方法设置为异步,以便调用方不必处理回调: // contrived example (edited in response to Servy's comment) public static Task<string> PromptForStringAsync(string prompt

刚刚获得VS2012并试图获得
async
的句柄

假设我有一个从阻塞源获取一些值的方法。我不希望该方法的调用方阻塞。我可以编写方法来接受回调,该回调在值到达时被调用,但由于我使用的是C#5,因此我决定将该方法设置为异步,以便调用方不必处理回调:

// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
    return Task.Factory.StartNew(() => {
        Console.Write(prompt);
        return Console.ReadLine();
    });
}
到目前为止还不错。问题在于调用GetNameAsync时:

public static void DoStuff()
{
    GetNameAsync();
    MainWorkOfApplicationIDontWantBlocked();
}
GetNameAsync
的要点在于它是异步的。我不想让它阻塞,因为我想尽快回到主工作应用程序DontWantBlocked,让GetNameAsync在后台完成它的工作。但是,以这种方式调用它会在
GetNameAsync
行上给我一个编译器警告:

Warning 1   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.
我非常清楚“当前方法的执行在调用完成之前继续”。这就是异步代码的要点,对吗

我更喜欢在没有警告的情况下编译代码,但是这里没有什么需要“修复”的,因为代码正是我想要它做的。我可以通过存储
GetNameAsync
的返回值来消除警告:

public static void DoStuff()
{
    var result = GetNameAsync(); // supress warning
    MainWorkOfApplicationIDontWantBlocked();
}
但现在我有多余的代码。VisualStudio似乎明白我是被迫编写这段不必要的代码的,因为它抑制了正常的“从未使用过的值”警告

我还可以通过在非异步方法中包装GetNameAsync来消除警告:

    public static Task GetNameWrapper()
    {
        return GetNameAsync();
    }
但这是更多余的代码。因此,我必须编写我不需要或容忍不必要的警告的代码


我使用async有什么地方不对吗?

是您的简化示例导致了超级代码。通常,您希望使用在程序中某个点从阻塞源获取的数据,因此您希望返回结果,以便能够获取数据


如果您确实有一些完全与程序的其余部分隔离的事情发生,那么异步将不是正确的方法。只需为该任务启动一个新线程。

如果您确实不需要结果,只需将
GetNameAsync
的签名更改为返回
void

public static async void GetNameAsync()
{
    ...
}
考虑查看相关问题的答案:

更新

如果需要结果,可以将
GetNameAsync
更改为返回,例如,
Task

公共静态异步任务GetNameAsync() { string firstname=wait PromptForStringAsync(“输入您的名字:”); string lastname=等待PromptForStringAsync(“输入您的姓氏:”); 返回firstname+lastname; } 并按如下方式使用:

public static void DoStuff()
{
    Task<string> task = GetNameAsync();

    // Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
    Task anotherTask = task.ContinueWith(r => {
            Console.WriteLine(r.Result);
        });

    MainWorkOfApplicationIDontWantBlocked();

    // OR wait for the result AFTER
    string result = task.Result;
}
publicstaticvoiddostuff()
{
任务Task=GetNameAsync();
//在MainWorkfaApplicationDonTwantBlocked之前设置延续
Task anotherTask=Task.ContinueWith(r=>{
控制台写入线(r.Result);
});
MainWorkfaApplication donTwantBlocked();
//或者在之后等待结果
字符串结果=task.result;
}

根据Microsoft关于此警告的文章,您只需将返回的任务分配给变量即可解决此问题。下面是Microsoft示例中提供的代码的翻译:

    // To suppress the warning without awaiting, you can assign the 
    // returned task to a variable. The assignment doesn't change how
    // the program runs. However, the recommended practice is always to
    // await a call to an async method.
    // Replace Call #1 with the following line.
    Task delayTask = CalledMethodAsync(delay);

请注意,这样做将导致ReSharper中出现“从不使用局部变量”消息。

这就是我目前正在做的:

SomeAyncFunction().RunConcurrently();
其中,
runconcurrence
定义为

 /// <summary> 
 /// Runs the Task in a concurrent thread without waiting for it to complete. This will start the task if it is not already running. 
 /// </summary> 
 /// <param name="task">The task to run.</param> 
 /// <remarks>This is usually used to avoid warning messages about not waiting for the task to complete.</remarks> 
 public static void RunConcurrently(this Task task) 
 { 
     if (task == null) 
         throw new ArgumentNullException("task", "task is null."); 

     if (task.Status == TaskStatus.Created) 
         task.Start(); 
 } 
//
///在并发线程中运行任务,而不等待任务完成。如果任务尚未运行,这将启动该任务。
///  
///要运行的任务。
///这通常用于避免关于未等待任务完成的警告消息。
公共静态void并发运行(此任务)
{ 
如果(任务==null)
抛出新ArgumentNullException(“任务”,“任务为空”);
if(task.Status==TaskStatus.Created)
task.Start();
} 


异步无效
不好

  • 我建议您通过匿名方法显式运行
    任务

    e、 g

    或者,如果您确实想阻止它,您可以等待匿名方法

    public static void DoStuff()
    {
        Task.Run(async () => await GetNameAsync());
        MainWorkOfApplicationThatWillBeBlocked();
    }
    
    但是,如果您的
    GetNameAsync
    方法必须与UI交互,甚至与任何UI绑定的东西交互(我正在看WINRT/MVVM),那么它会变得更有趣一些=)

    您需要像这样将引用传递给UI调度程序

    Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
    
    然后在异步方法中,您需要与UI或UI绑定元素交互,这些元素认为dispatcher

    dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {  this.UserName = userName; });
    

    我的讨论已经很晚了,但也可以选择使用
    #pragma
    预处理器指令。我有一些异步代码,在某些情况下我明确不想等待,我不喜欢警告和未使用的变量,就像你们其他人一样:

    #pragma warning disable 4014
    SomeMethodAsync();
    #pragma warning restore 4014
    
    “4014”
    来自此MSDN页面:

    另请参见此处@ryan horath的警告/回答

    异步调用期间引发的未等待的异常将丢失。要消除此警告,应将异步调用的任务返回值指定给变量。这确保您可以访问抛出的任何异常,这些异常将在返回值中指示

    C#7.0的更新 C#7.0添加了一个新特性,discard variables:,这在这方面也有帮助

    \uuxAE=SomeMethodAsync();
    
    我并不特别喜欢将任务分配给未使用的变量,或者更改方法签名以返回void的解决方案。前者会创建多余的、非直观的代码,而如果您正在实现一个接口或在希望使用返回任务的函数中有其他用法,则后者可能无法实现

    我的解决方案是创建一个名为DoNotAwait()的Task扩展方法,该方法不执行任何操作。这不仅会抑制所有警告,而且会
    Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
    
    dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {  this.UserName = userName; });
    
    #pragma warning disable 4014
    SomeMethodAsync();
    #pragma warning restore 4014
    
    public static class TaskExtensions
    {
        public static void DoNotAwait(this Task task) { }
    }
    
    public static void DoStuff()
    {
        GetNameAsync().DoNotAwait();
        MainWorkOfApplicationIDontWantBlocked();
    }
    
    public static class TasksExtensions
    {
        public static void RunAndForget(this Task task)
        {
        }
    }
    
    public static void DoStuff()
    {
        _ = GetNameAsync(); // we don't need the return value (suppresses warning)
        MainWorkOfApplicationIDontWantBlocked();
    }