programing

최상위 수준 요청에 대해 대기(거짓) 구성

powerit 2023. 5. 3. 21:58
반응형

최상위 수준 요청에 대해 대기(거짓) 구성

ConfigureAwait(false)를 최상위 요청에 사용해야 하는지 알아보고 있습니다.주제의 어느 정도 권위자로부터 이 게시물을 읽음: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

...그는 다음과 같은 것을 추천합니다.

public async Task<JsonResult> MyControllerAction(...)
{
    try
    {
        var report = await _adapter.GetReportAsync();
        return Json(report, JsonRequestBehavior.AllowGet);
    }
    catch (Exception ex)
    {
        return Json("myerror", JsonRequestBehavior.AllowGet);  // really slow without configure await
    }
}

public async Task<TodaysActivityRawSummary> GetReportAsync()
{           
    var data = await GetData().ConfigureAwait(false);

    return data
}

...최상위 호출을 제외한 모든 대기에서 ConfigureAwait(false)를 사용하라는 메시지가 표시됩니다.그러나 이 작업을 수행할 때 예외는 호출자에게 되돌아오는 데 몇 초가 걸립니다. 이 작업을 사용하고 사용한 후 바로 다시 돌아오게 하는 것과 비교할 수 없습니다.

비동기 메서드를 호출하는 MVC 컨트롤러 작업에 대한 최상의 방법은 무엇입니까?ConfigureAwait는 컨트롤러 자체에서 사용해야 합니까, 아니면 를 사용하여 데이터를 요청하는 서비스 호출에서만 사용해야 합니까?최상위 통화에서 사용하지 않으면 예외가 발생할 때까지 몇 초 동안 기다리는 것이 문제가 있는 것 같습니다.저는 HttpContext가 필요하지 않으며 컨텍스트가 필요하지 않을 경우 항상 ConfigureAwit(거짓)를 사용한다는 다른 게시물을 보았습니다.

업데이트: 통화 체인의 어딘가에 ConfigureAwait(거짓)가 누락되어 예외가 즉시 반환되지 않았습니다.그러나 ConfigureAwait(거짓)를 최상위 수준에서 사용해야 하는지 여부에 대한 질문은 여전히 게시된 상태로 남아 있습니다.

트래픽이 많은 웹사이트입니까?한 가지 가능한 설명은 당신이 경험하고 있다는 것입니다.ThreadPool사용하지 않을 때의 기아ConfigureAwait(false).없이.ConfigureAwait(false),await계속은 다음을 통해 대기열에 표시됩니다.AspNetSynchronizationContext.Post이 구현은 다음과 같이 요약됩니다.

Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action));
_lastScheduledTask = newTask; // the newly-created task is now the last one

여기서,ContinueWith사용되지 않습니다. TaskContinuationOptions.ExecuteSynchronously(저는 지속을 진정으로 비동기식으로 만들고 낮은 스택 조건의 가능성을 줄이는 것이 좋다고 생각합니다.따라서 다음에서 빈 스레드를 가져옵니다.ThreadPool계속 실행할 수 있습니다.이론적으로, 그것은 다음의 선행 과제가 발생하는 동일한 스레드일 수 있습니다.await끝났지만, 아마도 다른 스레드일 것입니다.

이 시점에서 ASP인 경우.NET 스레드 풀이 부족합니다(또는 새 스레드 요청을 수용하기 위해 확장해야 함). 지연이 발생할 수 있습니다.스레드 풀은 두 개의 하위 풀, 즉 IOCP 스레드와 작업자 스레드로 구성됩니다(자세한 내용은 이것과 이것을 참조하십시오).당신의.GetReportAsyncIOCP 스레드 서브풀에서 작업이 완료될 가능성이 높으며, 이는 굶주리지 않는 것으로 보입니다.OTOH, 그ContinueWithcontinuation은 worker 스레드 하위 풀에서 실행되며, 이 경우 굶주리고 있는 것으로 보입니다.

은 이런일일지않것다입니을어나은▁case 경우에는 일어나지 않습니다.ConfigureAwait(false)계속 사용됩니다.경우에는 그면다모, 두렇두.await연속 작업은 IOCP 또는 작업자 스레드에서 해당 선행 작업이 종료된 동일한 스레드에서 동기적으로 실행됩니다.

의 경우와 의 경우 모두에 대한 스레드 사용량을 비교할 수 있습니다.ConfigureAwait(false)저는 이 숫자가 더 클 것으로 예상합니다.ConfigureAwait(false)사용되지 않음:

catch (Exception ex)
{
    Log("Total number of threads in use={0}",
        Process.GetCurrentProcess().Threads.Count);
    return Json("myerror", JsonRequestBehavior.AllowGet);  // really slow without configure await
}

ASP의 크기를 늘려볼 수도 있습니다.NET 스레드 풀(최종 솔루션이 아닌 진단용)을 사용하여 설명된 시나리오가 실제로 해당되는지 확인합니다.

<configuration>
  <system.web>
    <applicationPool 
        maxConcurrentRequestsPerCPU="6000"
        maxConcurrentThreadsPerCPU="0" 
        requestQueueLimit="6000" />
  </system.web>
</configuration>

의견을 해결하기 위해 업데이트됨:

체인 어딘가에 Continue Await가 없다는 것을 깨달았습니다.이제 최상위 수준에서 ConfigureAwait(거짓)을 사용하지 않는 경우에도 예외를 던질 때 잘 작동합니다.

은 사용 가 차단 blocking constructs)를 수 을 시사합니다.Task.Result,Task.Wait,WaitHandle.WaitOne시간 초과 로직이 추가된 경우일 수 있습니다.그것들을 찾아봤나요?사용해 보십시오.Task.Run이 업데이트의 하단에서 제안합니다.게다가 스레드 수 진단을 수행하여 스레드 풀 부족/터덜거림을 배제할 것입니다.

그렇다면 최상위 레벨에서도 ContinueAwait를 사용하면 비동기의 모든 이점을 잃게 된다는 말씀입니까?

아니요, 그런 말이 아닙니다.의 전체 요점입니다.async무언가를 기다리는 동안 스레드를 차단하는 것을 피하는 것이며, 그 목표는 부가 가치와 상관없이 달성됩니다.ContinueAwait(false).

내 말은 사용하지 않는다는 것입니다. ConfigureAwait(false) 스위칭스위칭을 이될 수 . 이는가 될 수 .스레드 풀이 해당 용량에서 작동하는 경우 NET.그럼에도 불구하고 서버 확장성 측면에서 중복 스레드 스위치가 차단된 스레드보다 낫습니다.

공정하게 말하자면, 사용하면 특히 통화 체인 전반에서 일관성 없이 사용되는 경우 중복 컨텍스트 전환이 발생할 수도 있습니다.

그렇긴 하지만,ContinueAwait(false)또한 비동기 코드의 차단으로 인해 발생하는 교착 상태에 대한 해결책으로 종종 잘못 사용됩니다.그것이 제가 위에서 모든 코드 기반에서 차단 구조를 찾자고 제안한 이유입니다.

그러나 ConfigureAwait(거짓)를 최상위 수준에서 사용해야 하는지 여부에 대한 질문은 여전히 게시된 상태로 남아 있습니다.

Stephen Cleary가 이것에 대해 더 자세히 설명할 수 있기를 바랍니다. 제 생각은 이렇습니다.

항상 최상위 코드를 호출하는 "슈퍼 최상위" 코드가 있습니다.예를 들어, UI 앱의 경우, 그것은 다음을 호출하는 프레임워크 코드입니다.async void이벤트 핸들러.ASP의 경우.의 NET입니다.BeginExecute비동기 작업이 완료된 후 계속(있는 경우)이 올바른 동기화 컨텍스트에서 실행되도록 하는 것은 해당 슈퍼톱 수준 코드의 책임입니다.그것은 당신의 업무 코드의 책임이 아닙니다.예를 들어, 화재 및 망각과 같은 연속성이 전혀 없을 수 있습니다.async void이벤트 핸들러; 왜 그런 핸들러 내부의 컨텍스트를 복원하려고 합니까?

따라서, 당신의 최상위 방법 내에서, 만약 당신이 다음과 같은 맥락을 신경쓰지 않는다면.await 속연, 용사를 합니다.ConfigureAwait(false)당신이 할 수 있는 한 빨리.

할 수 .ConfigureAwait(false) 경우,를 "" " " " " " " "로 마무리하는 것이 .Task.Run뭐 그런 것들.미리 비동기 호출의 체인을 컨텍스트에서 제거하기 위해 이 작업을 수행합니다.

var report = await Task.Run(() =>
    _adapter.GetReportAsync()).ConfigureAwait(false);
return Json(report, JsonRequestBehavior.AllowGet);

이렇게 하면 스레드 스위치가 하나 더 추가되지만, 다음과 같은 경우 훨씬 더 많은 스레드 스위치를 절약할 수 있습니다.ConfigureAwait(false)에서 없사용됨이일성관부▁used됨 안에서 없이 됩니다.GetReportAsync또는 아이들의 전화.또한 콜 체인 내부의 차단 구조로 인해 발생하는 잠재적 교착 상태에 대한 해결책이 될 수 있습니다(있는 경우).

그러나 ASP에서는 주의해야 합니다.그물HttpContext.Current와 함께 흐르는 유일한 정적 속성이 아닙니다.AspNetSynchronizationContext를 들어예들어, 한또도 .Thread.CurrentThread.CurrentCulture맥락을 잃는 것에 대해 정말로 신경 쓰지 않도록 하세요.

의견을 해결하기 위해 업데이트됨:

브라우니 포인트의 경우 Configure Await(거짓)의 효과를 설명할 수 있습니다.보존되지 않은 컨텍스트는 무엇입니까?단지 HttpContext인가요, 아니면 클래스 객체의 로컬 변수인가요?

의모 로컬변의 async는 방은전로보다니존됩에 걸쳐 됩니다.await 암시적인 적인아니라뿐만것묵암▁well▁the▁as.this참조 - 설계에 의한.이들은 실제로 컴파일러 생성 비동기 상태 기계 구조로 캡처되므로 기술적으로 현재 스레드의 스택에 상주하지 않습니다.C# 대리인이 로컬 변수를 캡처하는 방법과 유사합니다. 사은.await은 그 로 그전대리입다니자된달계속자가체콜백에 입니다.ICriticalNotifyCompletion.UnsafeOnCompleted 중인 됨; (에 대해);Task와 함께.ConfigureAwait그것은)입니다.

OTOH, 대부분의 전역 상태(정적/TLS 변수, 정적 클래스 속성)는 대기 중에 자동으로 흐르지 않습니다.흐름을 얻는 항목은 특정 동기화 컨텍스트에 따라 달라집니다.하나가 없는 경우(또는 다음과 같은 경우)ConfigureAwait(false)사용됨), 으로 보존되는 유일한 글로벌 상태는 흐름에 의해 전달되는 것입니다.ExecutionContext마이크로소프트의 Stephen Toub는 "실행 컨텍스트 대 동기화 컨텍스트"라는 훌륭한 게시물을 게시했습니다.그는 언급합니다.SecurityContext그리고.Thread.CurrentPrincipal보안에 매우 중요합니다.되고 완전한 한 적이 ExecutionContext.

소스를 살짝 들여다보면 정확히 무엇이 흘러들어갔는지에 대해 자세히 알 수 있지만, 이러한 특정 구현에 의존해서는 안 됩니다.대신, 스티븐 클리어리(또는 )와 같은 것을 사용하여 항상 자신만의 전역 상태 흐름 논리를 만들 수 있습니다.NET 4.6).

아니면 극단적으로 말하자면, 당신은 또한 버림받을 수도 있습니다.ContinueAwait함께 사용자 지정 대기자를 만듭니다. 예를 들어, 이와 같습니다.그러면 계속 진행할 스레드/컨텍스트와 흐름 상태를 정확하게 제어할 수 있습니다.

그러나 ConfigureAwait(거짓)를 최상위 수준에서 사용해야 하는지 여부에 대한 질문은 여전히 게시된 상태로 남아 있습니다.

에 대한 .ConfigureAwait(false)나머지 방법이 맥락을 필요로 하지 않을 때마다 사용하는 것입니다.

ASP.NET에서 "콘텍스트"는 실제로 어느 곳에서도 잘 정의되어 있지 않습니다.다음과 같은 것들을 포함합니다.HttpContext.Current사용자 주체 및 사용자 문화.

그래서 이 질문은 다음과 같이 결론지어집니다: "그렇습니까?Controller.JsonASP가 필요합니다.NET 컨텍스트?"은 확실히 가능합니다.Json컨텍스트(자체 컨트롤러 구성원의 현재 응답을 쓸 수 있기 때문에)는 상관하지 않지만 OTOH는 사용자 문화를 다시 시작해야 할 수 있는 "포맷"을 수행합니다.

잘 모르겠어요.Json컨텍스트가 필요하지만, 이 컨텍스트는 어느 쪽이든 문서화되어 있지 않으며, 일반적으로 ASP에 대한 모든 호출을 가정합니다.NET 코드는 컨텍스트에 따라 다를 수 있습니다.그래서 나는 사용하지 않을 입니다.ConfigureAwait(false)내 컨트롤러 코드의 최상위 수준에서, 단지 안전한 쪽에 있기 위해.

언급URL : https://stackoverflow.com/questions/31225972/configureawaitfalse-on-top-level-requests

반응형