더 이상 ConfigureAwait(false)를 사용하지 않는 이유

5516 단어 asyncdotnetcsharp
이것은 인기가 없는 의견일 수 있지만 팀 코딩 표준에서 요구하지 않는 한 라이브러리를 포함하여 내가 작성하는 거의 모든 C# 코드에서 더 이상 ConfigureAwait(false)를 사용하지 않는 이유를 공유하고 싶습니다.

다음 두 가지는 원래 값task.ConfigureAwait(false)보다 호출ConfiguredTaskAwaitable 및 반환 task 값을 기다리는 주요 이점으로 널리 간주됩니다.
  • 잠재적인 성능 개선 사항
  • 교착 상태에 대한 잠재적인 구제책

  • 분해해 봅시다.

    잠재적인 성능 향상



    원래 동기화 컨텍스트/작업 스케줄러를 사용하여 await 연속 콜백을 대기열에 넣지 않고 실제 비동기 작업이 완료된 동일한 스레드에서 제자리에서 계속하기 때문일 수 있습니다.

    제 생각에 이것은 기본적으로 더 이상 동기화 컨텍스트가 없는 최신 ASP.NET Core/ASP.NET 5+ 백엔드와 관련성이 훨씬 낮습니다. ConfigureAwait(false) 해당 실행 환경에서 단순히 no-op이 됩니다.

    반면에 프론트엔드 코드에는 일반적으로 특정 UI 프레임워크별 동기화 컨텍스트가 설치된 메인 UI 스레드의 개념이 있습니다(예: DispatcherSynchronizationContext ).

    이 경우 UI 스레드에서 계속하지 않을 목적으로 ConfigureAwait(false)를 사용하는 것은 특히 대기 사이의 작은 코드 청크에 대해 시기상조 최적화라고 생각합니다. 빈번하지만 중복되는 컨텍스트 전환을 도입함으로써 실제로 부정적인 영향을 미칠 수 있습니다. 이를 설명하기 위해 some codemy old related question on SO이 있습니다.

    따라서 UI 스레드의 경우 특히 성능에 민감한 CPU 집약적인 코드의 일부인 실행 컨텍스트를 명시적으로 제어하는 ​​것을 선호합니다.

    나는 Task.Run(Func<Task> asyncFunc) 을 사용하여 그렇게합니다.

    await Task.Run(async () => 
    {
      await RunOneWorkflowAsync();
      await RunAnotherWorkflowAsync();
    });
    


    또는 최근에 TaskScheduler.SwitchTo 확장자의 a custom implementation으로 this David Fowler's initiative에서 영감을 얻었습니다.

    await TaskScheduler.Default.SwitchTo();
    await RunOneWorkflowAsync();
    await RunAnotherWorkflowAsync();
    


    대신에:

    await RunOneWorkflowAsync().ConfigureAwait(false);
    await RunAnotherWorkflowAsync();
    


    전자의 두 가지는 좀 더 장황하고 추가 스레드 전환이 발생할 수 있지만 의도를 명확하게 나타냅니다.

    또한 현재 동기화 컨텍스트/작업 스케줄러가 RunOneWorkflowAsync 에 미칠 수 있는 부작용과 RunOneWorkflowAsync 작성자가 구현에서 내부적으로 ConfigureAwait(false) 사용했는지 여부에 대해 걱정할 필요가 없습니다.

    두 번째 옵션인 TaskScheduler.Default.SwitchTo is optimized을 사용하면 현재 스레드가 이미 기본 작업 스케줄러를 사용하는 ThreadPool 스레드인지 확인하고, 그렇다면 동기적으로 완료합니다.

    교착 상태에 대한 잠재적인 해결책



    내 선택에 따라 교착 상태에 대한 방어 수단으로 ConfigureAwait(false)를 사용하면 실제로 모호한 버그를 숨길 수 있습니다. 교착 상태를 조기에 감지하는 것을 선호합니다. 최후의 수단으로 교착 상태가 발생하기 쉬운 코드의 래퍼로 Task.Run를 계속 사용합니다. 여기a real-life example가 필요했습니다.

    또한 디버깅 및 단위 테스트 전망에서 비동기 코드 디버깅을 위해 사용자 정의SynchronizationContext 구현을 설치할 수 있는 옵션이 항상 있으며 이를 위해서는 포기해야 합니다ConfigureAwait(false).

    결론



    Microsoft의 Stephen Toub는 그의 뛰어난"ConfigureAwait FAQ"에서 .NET Core 이상만 대상으로 하는 경우에도 컨텍스트에 구애받지 않는 범용 라이브러리에 대해 여전히 ConfigureAwait(false)를 사용할 것을 권장합니다. 나중에 해당 블로그 게시물에 대한 주석David Fowler mentions에서 "대부분의 ASP.NET Core는 ConfigureAwait(false)를 사용하지 않으며 불필요한 것으로 간주되었기 때문에 명시적인 결정이었습니다."

    특정 프로젝트에 필요한지 여부에 대한 최선의 판단을 사용하십시오. 저는 개인적으로 가능한 경우 피하고ConfigureAwait(false) 의미가 있는 곳에서 실행 컨텍스트를 명시적으로 제어하기로 선택했습니다.

    라이브러리 코드는 어떤 상황에서도 잘 작동해야 한다고 생각하며 감지되지 않은 교착 상태에 대한 방어책으로 ConfigureAwait(false)를 사용하는 아이디어가 마음에 들지 않습니다. 성능 면에서 ASP.NET Core와 같이 중요한 경우 최신 .NET에는 이미 동기화 컨텍스트가 없습니다.

    추가 보너스로 내 C# 코드는 ConfigureAwait(false) 없이 더 깨끗해 보입니다 :-)

    좋은 웹페이지 즐겨찾기