IT story

이 호출이 기다리고 있지 않다는 경고, 현재 메소드의 실행은 계속됩니다

hot-time 2020. 7. 13. 07:57
반응형

이 호출이 기다리고 있지 않다는 경고, 현재 메소드의 실행은 계속됩니다


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();
    });
}

다음은이를 호출하는 예제 메소드입니다. 경우 PromptForStringAsync비동기 아니었다,이 방법은 콜백 내에서 콜백을 중첩 필요합니다. 비동기를 사용하면이 방법을 매우 자연스럽게 작성할 수 있습니다.

public static async Task GetNameAsync()
{
    string firstname = await PromptForStringAsync("Enter your first name: ");
    Console.WriteLine("Welcome {0}.", firstname);

    string lastname = await PromptForStringAsync("Enter your last name: ");
    Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}

여태까지는 그런대로 잘됐다. 문제는 GetNameAsync를 호출 할 때입니다 .

public static void DoStuff()
{
    GetNameAsync();
    MainWorkOfApplicationIDontWantBlocked();
}

요점은 GetNameAsync비동기 적이라는 것입니다. MainWorkOfApplicationIDontWantBlocked ASAP로 돌아가서 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();
}

But now I have superfluous code. Visual Studio seems to understand that I was forced to write this unnecessary code, because it suppresses the normal "value never used" warning.

I can also get rid of the warning by wrapping GetNameAsync in a method that's not async:

    public static Task GetNameWrapper()
    {
        return GetNameAsync();
    }

But that's even more superfluous code. So I have to write code I don't need or tolerate an unnecessary warning.

Is there something about my use of async that's wrong here?


If you really don't need the result, you can simply change the GetNameAsync's signature to return void:

public static async void GetNameAsync()
{
    ...
}

Consider to see answer to a related question: What's the difference between returning void and returning a Task?

Update

If you need the result, you can change the GetNameAsync to return, say, Task<string>:

public static async Task<string> GetNameAsync()
{
    string firstname = await PromptForStringAsync("Enter your first name: ");
    string lastname = await PromptForStringAsync("Enter your last name: ");
    return firstname + lastname;
}

And use it as follows:

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;
}

I'm quite late to this discussion, but there is also the option to use the #pragma pre-processor directive. I have some async code here and there that I explicitly do not want to await in some conditions, and I dislike warnings and unused variables just like the rest of you:

#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014

The "4014" comes from this MSDN page: Compiler Warning (level 1) CS4014.

See also the warning/answer by @ryan-horath here https://stackoverflow.com/a/12145047/928483.

Exceptions thrown during an async call that is not awaited will be lost. To get rid of this warning, you should assign the Task return value of the async call to a variable. This ensures you have access to any exceptions thrown, which will be indicated in the return value.

Update for C# 7.0

C# 7.0 adds a new feature, discard variables: Discards - C# Guide, which can also help in this regard.

var _ = SomeMethodAsync();

I'm not particularly fond of the solutions that either assign the task to an unused variable, or changing the method signature to return void. The former creates superfluous, non-intuitive code, while the latter may not be possible if you're implementing an interface or have another usage of the function where you want to use the returned Task.

My solution is to create an extension method of Task, called DoNotAwait() that does nothing. This will not only suppress all warnings, ReSharper or otherwise, but makes the code more understandable, and indicates to future maintainers of your code that you really intended for the call to not be awaited.

Extension method:

public static class TaskExtensions
{
    public static void DoNotAwait(this Task task) { }
}

Usage:

public static void DoStuff()
{
    GetNameAsync().DoNotAwait();
    MainWorkOfApplicationIDontWantBlocked();
}

Edited to add: this is similar to Jonathan Allen's solution where the extension method would start the task if not already started, but I prefer to have single-purpose functions so that the caller's intent is completely clear.


async void IS BAD!

  1. What's the difference between returning void and returning a Task?
  2. https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

What I suggest is that you explicitly run the Task via an anonymous method...

e.g.

public static void DoStuff()
{
    Task.Run(async () => GetNameAsync());
    MainWorkOfApplicationIDontWantBlocked();
}

Or if you did want it to block you can await on the anonymous method

public static void DoStuff()
{
    Task.Run(async () => await GetNameAsync());
    MainWorkOfApplicationThatWillBeBlocked();
}

However, if your GetNameAsync method has to interact with UI or even anything UI bound, (WINRT/MVVM, I'm looking at you), then it gets a little funkier =)

You'll need to pass the reference to the UI dispatcher like this...

Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));

And then in your async method you'll need to interact with your UI or UI bound elements thought that dispatcher...

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

This is what I'm currently doing:

SomeAyncFunction().RunConcurrently();

Where RunConcurrently is defined as...

 /// <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(); 
 } 

https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs

https://www.nuget.org/packages/Tortuga.Anchor/


According to the Microsoft article on this warning, you can solve it by simply assigning the returned task to a variable. Below is a translation of the code provided in the Microsoft example:

    // 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);

Note that doing this will result in the "Local variable is never used" message in ReSharper.


Here, a simple solution.

public static class TasksExtensions
{
    public static void RunAndForget(this Task task)
    {
    }
}

Regards


It's your simplified example that causes the superflous code. Normally you would want to use the data that was fetched from the blocking source at some point in the program, so you would want the result back so that it would be possible to get to the data.

If you really have something that happens totally isolated from the rest of the program, async would not be the right approach. Just start a new thread for that task.


Do you really want to ignore the result? as in including ignoring any unexpected exceptions?

If not you might want a look at this question: Fire and Forget approach,


If you don't want to change the method signature to return void (as returning void should always be avoided), you can use C# 7.0+ Discard feature like this, which is slightly better than assigning to a variable (and should remove most other source validation tools warnings):

public static void DoStuff()
{
    _ = GetNameAsync(); // we don't need the return value (suppresses warning)
    MainWorkOfApplicationIDontWantBlocked();
}

참고URL : https://stackoverflow.com/questions/14903887/warning-this-call-is-not-awaited-execution-of-the-current-method-continues

반응형