컴파일러 경고(수준 1) CS4014

이 호출이 대기되지 않으므로 호출이 완료되기 전에 현재 메서드가 계속 실행됩니다. await 연산자는 호출 결과에 적용하는 것이 좋습니다.

현재 메서드는 Task 또는 Task<TResult>를 반환하는 비동기 메서드를 호출하고 결과에 await 연산자를 적용하지 않습니다. 비동기 메서드 호출이 비동기 작업을 시작합니다. 그러나 await 연산자가 적용되지 않기 때문에 프로그램이 작업이 완료될 때까지 기다리지 않고 계속됩니다. 대부분의 경우 예상대로 동작하지 않습니다. 대개 호출 메서드의 다른 부분은 호출의 결과에 따라 다르거나, 최소한 호출을 포함하는 메서드에서 돌아오기 전에 호출된 메서드가 완료되어야 합니다.

호출된 비동기 메서드에서 발생하는 예외가 어떻게 되는지도 중요한 문제입니다. Task 또는 Task<TResult>을(를) 반환하는 메서드에서 발생하는 예외는 반환된 작업에 저장됩니다. 작업을 기다리지 않거나 예외를 명시적으로 확인하지 않는 경우 예외가 손실됩니다. 작업을 기다리는 경우 예외가 다시 throw됩니다.

가장 좋은 방법은 항상 호출을 기다리는 것입니다.

비동기 호출이 완료될 때까지 기다리지 않으려고 하거나 호출된 메서드가 예외를 발생시키지 않는 경우에만 경고가 표시되지 않게 해야 합니다. 이 경우 변수에 호출의 작업 결과를 할당하여 경고가 표시되지 않게 할 수 있습니다.

다음 예제는 경고를 발생시키는 방법, 경고가 표시되지 않게 하는 방법 및 호출을 대기하는 방법을 보여줍니다.

static async Task CallingMethodAsync(int millisecondsDelay)
{
    Console.WriteLine("  Entering calling method.");

    // Call #1.
    // Call an async method. Because you don't await it, its completion
    // isn't coordinated with the current method, CallingMethodAsync.
    // The following line causes warning CS4014.
    CalledMethodAsync(millisecondsDelay);

    // Call #2.
    // 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, recommended practice is always to
    // await a call to an async method.

    // Replace Call #1 with the following line.
    // Task delayTask = CalledMethodAsync(millisecondsDelay);

    // Call #3
    // To contrast with an awaited call, replace the unawaited call
    // (Call #1 or Call #2) with the following awaited call. Best
    // practice is to await the call.

    // await CalledMethodAsync(millisecondsDelay);

    Console.WriteLine("  Returning from calling method.");
}

static async Task CalledMethodAsync(int millisecondsDelay)
{
    Console.WriteLine("    Entering called method, starting and awaiting Task.Delay.");

    await Task.Delay(millisecondsDelay);

    Console.WriteLine("    Task.Delay is finished--returning from called method.");
}

예제에서는 Call #1 또는 Call #2를 선택할 경우 해당 호출자 CalledMethodAsync 및 호출자의 호출자가 둘 다 완료된 후 기다리지 않는 비동기 메서드 CallingMethodAsync가 완료됩니다. 다음 출력의 마지막 줄은 호출된 메서드가 끝나는 시기를 보여줍니다. 전체 예제에서 CallingMethodAsync 를 호출하는 이벤트 처리기의 시작과 종료가 출력에 표시되어 있습니다.

Entering the Click event handler.
  Entering calling method.
    Entering called method, starting and awaiting Task.Delay.
  Returning from calling method.
Exiting the Click event handler.
    Task.Delay is finished--returning from called method.

#pragma warning 지시문을 사용하여 컴파일러 경고가 표시되지 않게 할 수도 있습니다.

예시

다음 콘솔 애플리케이션은 이전 예제의 메서드를 포함합니다. 다음 단계는 애플리케이션을 설정합니다.

  1. 새 콘솔 애플리케이션을 만들고 이름을 AsyncWarning으로 지정합니다.

  2. Visual Studio Code 편집기에서 Program.cs 파일을 선택합니다.

  3. Program.cs의 코드를 다음 코드로 바꿉니다.

    using System;
    using System.Threading.Tasks;
    
    namespace AsyncWarning
    {
        class Program
        {
            static async Task Main()
            {
                Console.WriteLine("Entering Main() application entry point.");
    
                int millisecondsDelay = 2000;
                await CallingMethodAsync(millisecondsDelay);
    
                Console.WriteLine("Exiting Main() application entry point.");
    
                await Task.Delay(millisecondsDelay + 500);
            }
    
            static async Task CallingMethodAsync(int millisecondsDelay)
            {
                Console.WriteLine("  Entering calling method.");
    
                // Call #1.
                // Call an async method. Because you don't await it, its completion
                // isn't coordinated with the current method, CallingMethodAsync.
                // The following line causes warning CS4014.
                // CalledMethodAsync(millisecondsDelay);
    
                // Call #2.
                // 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, recommended practice is always to
                // await a call to an async method.
    
                // Replace Call #1 with the following line.
                //Task delayTask = CalledMethodAsync(millisecondsDelay);
    
                // Call #3
                // To contrast with an awaited call, replace the unawaited call
                // (Call #1 or Call #2) with the following awaited call. Best
                // practice is to await the call.
    
                // await CalledMethodAsync(millisecondsDelay);
    
                Console.WriteLine("  Returning from calling method.");
            }
    
            static async Task CalledMethodAsync(int millisecondsDelay)
            {
                Console.WriteLine("    Entering called method, starting and awaiting Task.Delay.");
    
                await Task.Delay(millisecondsDelay);
    
                Console.WriteLine("    Task.Delay is finished--returning from called method.");
            }
        }
    
        // Output with Call #1 or Call #2. (Wait for the last line to appear.)
    
        // Entering Main() application entry point.
        //   Entering calling method.
        //     Entering called method, starting and awaiting Task.Delay.
        //   Returning from calling method.
        // Exiting Main() application entry point.
        //     Task.Delay is finished--returning from called method.
    
        // Output with Call #3, which awaits the call to CalledMethodAsync.
    
        // Entering Main() application entry point.
        //   Entering calling method.
        //     Entering called method, starting and awaiting Task.Delay.
        //     Task.Delay is finished--returning from called method.
        //   Returning from calling method.
        // Exiting Main() application entry point.
    }
    
  4. F5 키를 선택하여 프로그램을 실행합니다.

예상 출력이 코드의 끝에 나타납니다.

참고 항목