[C#] async / await-언제 작업 대 무효를 반환합니까?

어떤 시나리오에서 사용하고 싶습니까?

public async Task AsyncMethod(int num)

대신에

public async void AsyncMethod(int num)

당신의 진행 상황을 추적 할 수 있도록 작업을해야 할 경우 내가 생각할 수있는 유일한 시나리오입니다.

또한, 다음과 같은 방법으로, 비동기 및 await를 키워드는 불필요하다?

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}



답변

1) 일반적으로 a를 반환하려고합니다 Task. 이벤트의 경우 리턴 유형 이 필요한 경우는 예외 void입니다. 발신자 await에게 작업 을 허용하지 않을 이유가 없다면 왜 허용하지 않습니까?

2) async리턴 void하는 메소드 는 다른 측면에서 특별합니다. 메소드는 최상위 비동기 조작 을 나타내며 태스크가 예외를 리턴 할 때 적용되는 추가 규칙이 있습니다. 차이점을 보여주는 가장 쉬운 방법은 예제를 사용하는 것입니다.

static async void f()
{
    await h();
}

static async Task g()
{
    await h();
}

static async Task h()
{
    throw new NotImplementedException();
}

private void button1_Click(object sender, EventArgs e)
{
    f();
}

private void button2_Click(object sender, EventArgs e)
{
    g();
}

private void button3_Click(object sender, EventArgs e)
{
    GC.Collect();
}

f예외는 항상 “관측”됩니다. 최상위 비동기 메소드를 떠나는 예외는 처리되지 않은 다른 예외처럼 간단하게 처리됩니다. g의 예외는 관찰되지 않습니다. 가비지 수집기가 작업을 정리하면 해당 작업에서 예외가 발생하여 예외를 처리 한 사람이 없음을 알 수 있습니다. 그렇게되면, TaskScheduler.UnobservedTaskException핸들러가 실행됩니다. 이 일어나게해서는 안됩니다. 예를 사용하려면

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}

예, 사용 async하고 await여기에, 그들은 예외가 발생하는 경우 반드시 당신의 방법은 여전히 제대로 작동합니다.

자세한 내용은 참조하십시오 : http://msdn.microsoft.com/en-us/magazine/jj991977.aspx


답변

나는 Jérôme Laban이 작성 async하고 void작성한 이 매우 유용한 기사를 보았습니다 :
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void .html

결론 async+void은 시스템을 중단시킬 수 있으며 일반적으로 UI 측 이벤트 핸들러에서만 사용해야한다는 것입니다.

그 이유는 AsyncVoidMethodBuilder가 사용하는 동기화 컨텍스트이기 때문에이 예제에서는 없습니다. 앰비언트 동기화 컨텍스트가없는 경우 비동기 void 메소드 본문에서 처리되지 않은 예외는 ThreadPool에서 다시 발생합니다. 처리되지 않은 예외가 발생할 수있는 다른 논리적 장소는 없지만, 불행하게도 ThreadPool의 처리되지 않은 예외는 .NET 2.0 이후 프로세스를 효과적으로 종료하기 때문에 프로세스가 종료되는 것입니다. 당신은 AppDomain.UnhandledException 이벤트를 사용하여 모든 처리되지 않은 예외를 가로 챌 수 있지만,이 이벤트에서 프로세스를 복구 할 방법이 없습니다.

UI 이벤트 핸들러를 작성할 때 예외가 비동기가 아닌 메소드에서 발견 된 것과 동일한 방식으로 처리되므로 비동기 void 메소드는 아무 문제가 없습니다. 그들은 디스패처에 던져집니다. 이러한 예외를 복구 할 가능성이 있으며 대부분의 경우 올바른 것 이상입니다. 그러나 UI 이벤트 핸들러 외부에서 비동기 void 메소드는 사용하기에 위험하며 찾기가 쉽지 않을 수 있습니다.


답변

나는이 진술에서 분명한 아이디어를 얻었다.

  1. 비동기 void 메소드에는 서로 다른 오류 처리 의미가 있습니다. 비동기 작업 또는 비동기 작업 메서드에서 예외가 발생하면 해당 예외가 캡처되어 Task 개체에 배치됩니다. 비동기 void 메소드에는 Task 객체가 없으므로 비동기 void 메소드에서 발생하는 예외는 SynchronizationContext (SynchronizationContext는 “where”코드가 실행될 수있는 위치를 나타냄)에서 직접 발생합니다. 시작

비동기 공허 메서드의 예외는 캐치로 잡을 수 없습니다

private async void ThrowExceptionAsync()
{
  throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
  try
  {
    ThrowExceptionAsync();
  }
  catch (Exception)
  {
    // The exception is never caught here!
    throw;
  }
}

이러한 예외는 AppDomain.UnhandledException 또는 GUI / ASP.NET 응용 프로그램에 대한 유사한 catch-all 이벤트를 사용하여 관찰 할 수 있지만 정기적 인 예외 처리를 위해 해당 이벤트를 사용하는 것은 유지 보수를위한 레시피입니다 (응용 프로그램이 충돌 함).

  1. 비동기 void 메소드에는 서로 다른 구성 의미가 있습니다. Task 또는 Task를 반환하는 비동기 메서드는 await, Task.WhenAny, Task.WhenAll 등을 사용하여 쉽게 구성 할 수 있습니다. void를 반환하는 비동기 메서드는 호출 코드가 완료되었음을 알리는 쉬운 방법을 제공하지 않습니다. 여러 개의 비동기 void 메소드를 시작하는 것은 쉽지만 완료 시점을 결정하는 것은 쉽지 않습니다. 비동기 void 메소드는 시작 및 완료시 SynchronizationContext에 알리지 만, 사용자 정의 SynchronizationContext는 일반 애플리케이션 코드를위한 복잡한 솔루션입니다.

  2. 비동기 이벤트 핸들러를 사용할 때 동기 무효화 메소드는 SynchronizationContext에서 직접 예외를 발생시키기 때문에 유용합니다. 이는 동기 이벤트 핸들러가 작동하는 방식과 유사합니다.

자세한 내용은이 링크를 확인을 위해
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx


답변

async void를 호출하는 문제는 작업을 다시받지 못하고 기능 작업이 완료된 시점을 알 방법이 없다는 것입니다 ( https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/ ? p = 96655 )

비동기 함수를 호출하는 세 가지 방법은 다음과 같습니다.

async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }

모든 경우에 함수는 일련의 작업으로 변환됩니다. 차이는 어떤 함수가 반환됩니다.

첫 번째 경우, 함수는 결국 t를 생성하는 작업을 반환합니다.

두 번째 경우, 함수는 제품이없는 태스크를 리턴하지만 완료 될 때까지 알기 위해 계속 기다릴 수 있습니다.

세 번째 사례는 불쾌한 사례입니다. 세 번째 경우는 두 번째 경우와 비슷하지만 작업을 다시받지 못합니다. 함수의 작업이 완료된 시점을 알 방법이 없습니다.

비동기 무효 사례는 “화재와 잊어 버리기”입니다. 작업 체인을 시작하지만 완료 시점에 대해서는 신경 쓰지 않습니다. 함수가 반환되면 처음으로 기다리는 모든 것이 실행되었다는 것입니다. 첫 번째 대기 이후의 모든 항목은 나중에 액세스 할 수없는 지정되지 않은 지점에서 실행됩니다.


답변

async void예외를 잡는 데주의를 기울이는 한 백그라운드 작업을 시작 하는 데 사용할 수 있다고 생각합니다 . 생각?

class Program {

    static bool isFinished = false;

    static void Main(string[] args) {

        // Kick off the background operation and don't care about when it completes
        BackgroundWork();

        Console.WriteLine("Press enter when you're ready to stop the background operation.");
        Console.ReadLine();
        isFinished = true;
    }

    // Using async void to kickoff a background operation that nobody wants to be notified about when it completes.
    static async void BackgroundWork() {
        // It's important to catch exceptions so we don't crash the appliation.
        try {
            // This operation will end after ten interations or when the app closes. Whichever happens first.
            for (var count = 1; count <= 10 && !isFinished; count++) {
                await Task.Delay(1000);
                Console.WriteLine($"{count} seconds of work elapsed.");
            }
            Console.WriteLine("Background operation came to an end.");
        } catch (Exception x) {
            Console.WriteLine("Caught exception:");
            Console.WriteLine(x.ToString());
        }
    }
}


답변

내 대답은 간단합니다. void 메소드를 기다릴 수 없습니다.

Error   CS4008  Cannot await 'void' TestAsync   e:\test\TestAsync\TestAsyncProgram.cs

따라서 메소드가 비동기 인 경우 비동기 이점을 잃을 수 있기 때문에 대기하는 것이 좋습니다.


답변

Microsoft 문서에 따르면 절대 사용해서는 안됩니다async void

다음을 수행하지 마십시오. 다음 예제는 async void첫 번째 대기에 도달 할 때 HTTP 요청을 완료하는 데 사용합니다.

  • ASP.NET Core 앱에서는 항상 나쁜 습관입니다.

  • HTTP 요청이 완료된 후 HttpResponse에 액세스합니다.

  • 프로세스가 충돌합니다.