programing

HttpClient.대기/비동기화를 사용할 때 GetAsync(...)가 반환되지 않음

padding 2023. 6. 12. 21:12
반응형

HttpClient.대기/비동기화를 사용할 때 GetAsync(...)가 반환되지 않음

편집: 질문은 동일한 문제일 수 있지만 응답이 없습니다...

편집: 테스트 사례 5에서 작업이 고착된 것으로 나타납니다.WaitingForActivation주.

시스템을 사용하는 동안 이상한 동작이 발생했습니다.http.http.. 4 - 여기서 .NET 4.5 .4.5)에 대한 를 ". HttpClient - "대기"("):httpClient.GetAsync(...)다시는 돌아오지 않을 것입니다.

이 문제는 새로운 비동기/대기 언어 기능 및 작업 API를 사용할 때만 발생합니다. 코드는 계속만 사용할 때 항상 작동하는 것 같습니다.

다음은 문제를 재현하는 코드입니다. Visual Studio 11의 새로운 "MVC 4 WebApi 프로젝트"에 이 코드를 삭제하여 다음 GET 끝점을 표시합니다.

/api/test1
/api/test2
/api/test3
/api/test4
/api/test5 <--- never completes
/api/test6

서 각 stackoverflow.com 의 응답 하고 동일한 .com 의 헤더)를합니다./api/test5그것은 결코 완성되지 않습니다.

HttpClient 클래스에서 버그가 발생했거나 API를 잘못 사용하고 있습니까?

재생산할 코드:

public class BaseApiController : ApiController
{
    /// <summary>
    /// Retrieves data using continuations
    /// </summary>
    protected Task<string> Continuations_GetSomeDataAsync()
    {
        var httpClient = new HttpClient();

        var t = httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);

        return t.ContinueWith(t1 => t1.Result.Content.Headers.ToString());
    }

    /// <summary>
    /// Retrieves data using async/await
    /// </summary>
    protected async Task<string> AsyncAwait_GetSomeDataAsync()
    {
        var httpClient = new HttpClient();

        var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);

        return result.Content.Headers.ToString();
    }
}

public class Test1Controller : BaseApiController
{
    /// <summary>
    /// Handles task using Async/Await
    /// </summary>
    public async Task<string> Get()
    {
        var data = await Continuations_GetSomeDataAsync();

        return data;
    }
}

public class Test2Controller : BaseApiController
{
    /// <summary>
    /// Handles task by blocking the thread until the task completes
    /// </summary>
    public string Get()
    {
        var task = Continuations_GetSomeDataAsync();

        var data = task.GetAwaiter().GetResult();

        return data;
    }
}

public class Test3Controller : BaseApiController
{
    /// <summary>
    /// Passes the task back to the controller host
    /// </summary>
    public Task<string> Get()
    {
        return Continuations_GetSomeDataAsync();
    }
}

public class Test4Controller : BaseApiController
{
    /// <summary>
    /// Handles task using Async/Await
    /// </summary>
    public async Task<string> Get()
    {
        var data = await AsyncAwait_GetSomeDataAsync();

        return data;
    }
}

public class Test5Controller : BaseApiController
{
    /// <summary>
    /// Handles task by blocking the thread until the task completes
    /// </summary>
    public string Get()
    {
        var task = AsyncAwait_GetSomeDataAsync();

        var data = task.GetAwaiter().GetResult();

        return data;
    }
}

public class Test6Controller : BaseApiController
{
    /// <summary>
    /// Passes the task back to the controller host
    /// </summary>
    public Task<string> Get()
    {
        return AsyncAwait_GetSomeDataAsync();
    }
}

API를 잘못 사용하고 있습니다.

ASP.NET에서는 한 번에 하나의 스레드만 요청을 처리할 수 있습니다.필요한 경우 일부 병렬 처리를 수행할 수 있지만(스레드 풀에서 추가 스레드를 빌림), 하나의 스레드에만 요청 컨텍스트가 있습니다(추가 스레드에는 요청 컨텍스트가 없습니다).

이것은 ASP.NET에 의해 관리됩니다.

기본적으로 다음과 같은 경우await a Task캡처된 상태에서 메소드가 다시 시작됩니다.SynchronizationContext됨)TaskSchedulerSynchronizationContext입니다: 은 "", "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""await다시 시작하면 요청 컨텍스트와 함께 다시 시작됩니다.

그래서, 여기 이유가 있습니다.test5실패:실패:

  • Test5Controller.Get행실을 합니다.AsyncAwait_GetSomeDataAsync(ASP.NET 요청 컨텍스트 내).
  • AsyncAwait_GetSomeDataAsync행실을 합니다.HttpClient.GetAsync(ASP.NET 요청 컨텍스트 내).
  • 되고, 요청이 전송됩니다.HttpClient.GetAsync완료되지 않은 항목을 반환합니다.Task.
  • AsyncAwait_GetSomeDataAsync기다리고 있습니다.Task않기 에, 아직완성않때에문기았,AsyncAwait_GetSomeDataAsync완료되지 않은 항목을 반환합니다.Task.
  • Test5Controller.Get 때까지 현재 스레드를 차단합니다.Task완료합니다.
  • 응답이 옵니다.Task 된환반이 했습니다.HttpClient.GetAsync완료되었습니다.
  • AsyncAwait_GetSomeDataAsyncASP.NET 요청 컨텍스트 내에서 재개를 시도합니다.. 즉, 그나해 러당컨가있습다니스레드이미에트텍스▁▁thread다▁the▁however▁in에 차단된 스레드입니다. 스레드가 다음에서 차단되었습니다.Test5Controller.Get.
  • 교착 상태.

다른 솔루션이 작동하는 이유는 다음과 같습니다.

  • (test1,test2,그리고.test3):Continuations_GetSomeDataAsyncASP.NET 요청 컨텍스트 외부에서 스레드 풀로의 계속을 예약합니다.이를 통해Task 된환반이 했습니다.Continuations_GetSomeDataAsync요청 컨텍스트를 다시 입력하지 않고 완료할 수 있습니다.
  • (test4그리고.test6 ): 이후TaskASP.NET 요청 스레드가 차단되지 않을 때까지 기다립니다.이것은 허용합니다.AsyncAwait_GetSomeDataAsync계속할 준비가 되었을 때 ASP.NET 요청 컨텍스트를 사용합니다.

다음은 모범 사례입니다.

  1. 에서 신의라 "이브러리"async: " ", ", 용사 "ConfigureAwait(false)가급적이면 언제든지당신의 경우, 이것은 바뀔 것입니다.AsyncAwait_GetSomeDataAsync되려고var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
  2. 에서 Task입니다; 그은것은.async맨 아래까지.다른 말로 하면,awaitGetResult(Task.Result그리고.Task.Wait또한 다음으로 대체해야 합니다.await).

두 이점을 얻을 수: (나머지 부분).AsyncAwait_GetSomeDataAsyncmethod컨텍스트에 가 없는 풀 되며, 는 method)와 ASP.NET 파일 이름은 동일합니다.async(요청 스레드를 차단하지 않음).

추가 정보:

2012-07-13 업데이트: 이 답변을 블로그 게시물통합했습니다.

편집: 일반적으로 교착 상태를 피하기 위한 마지막 배수로 작업을 제외하고는 아래 작업을 피하려고 합니다.Stephen Cleary의 첫 번째 의견을 읽으십시오.

여기서부터 빠른 수정.쓰는 대신:

Task tsk = AsyncOperation();
tsk.Wait();

시도:

Task.Run(() => AsyncOperation()).Wait();

또는 결과가 필요한 경우:

var result = Task.Run(() => AsyncOperation()).Result;

원본(위 예제와 일치하도록 편집됨):

이제 SyncOperation은 SynchronizationContext가 없는 스레드 풀에서 호출되고 AsyncOperation 내부에서 사용되는 연속이 강제로 호출 스레드로 되돌아가지 않습니다.

비동기식으로 만들 수 있는 옵션이 없기 때문에 사용할 수 있는 옵션으로 보입니다.

원본:

FooAsync 메서드의 대기가 다시 마샬링할 컨텍스트를 찾지 못하도록 합니다.이렇게 하는 가장 간단한 방법은 태스크에서 호출을 래핑하는 것과 같이 스레드 풀에서 비동기 작업을 호출하는 것입니다.예를 들어, 실행.

intSync() {태스크 반환.실행((() => 라이브러리.FooAsync().결과; }

FooAsync는 이제 동기화 컨텍스트가 없는 스레드 풀에서 호출되고 FooAsync 내부에서 사용되는 연속 작업이 동기화()를 호출하는 스레드로 강제되지 않습니다.

를 사용하고 에..Result또는.Wait또는await이것은 당신의 코드에 교착 상태를 초래할 것입니다.

사용할 수 있습니다.ConfigureAwait(false)async교착 상태를 방지하는 방법

다음과 같이:

var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead)
                             .ConfigureAwait(false);

사용할 수 있습니다.ConfigureAwait(false)가능한 경우 비동기 코드 차단 안 함을 선택합니다.

이 두 학교는 정말로 제외되지 않습니다.

다음은 단순히 사용해야 하는 시나리오입니다.

   Task.Run(() => AsyncOperation()).Wait(); 

또는 그와 비슷한 것.

   AsyncContext.Run(AsyncOperation);

데이터베이스 트랜잭션 속성 아래에 있는 MVC 작업이 있습니다.그 생각은 (아마도) 무언가 잘못되면 행동에서 행해진 모든 것을 되돌리는 것이었습니다.이렇게 하면 컨텍스트 전환이 허용되지 않으며, 그렇지 않으면 트랜잭션 롤백 또는 커밋 자체가 실패합니다.

제가 필요한 라이브러리는 비동기식으로 실행될 것으로 예상되므로 비동기식입니다.

유일한 선택지.일반 동기화 호출로 실행합니다.

나는 그저 각자에게 말하고 있는 것뿐입니다.

저는 이것을 OP와의 직접적인 관련성보다는 완성도를 위해 여기에 넣을 것입니다.거의 하루 동안 컴퓨터를 디버깅했습니다.HttpClient왜 내가 답장을 못 받았는지 궁금해하는 요청.

가 잊고 되었습니다.await그자리의 async콜 스택에서 더 아래쪽으로 콜합니다.

세미콜론을 놓친 것만큼이나 기분이 좋습니다.

저의 경우 'wait'가 요청 실행 중 예외로 인해 완료되지 않았습니다(예: 서버가 응답하지 않음).그것을 시도로 둘러싸세요.무슨 일이 일어났는지 파악하기 위한 캐치, 그것은 또한 당신의 '실수'를 우아하게 완성할 것입니다.

public async Task<Stuff> GetStuff(string id)
{
    string path = $"/api/v2/stuff/{id}";
    try
    {
        HttpResponseMessage response = await client.GetAsync(path);
        if (response.StatusCode == HttpStatusCode.OK)
        {
            string json = await response.Content.ReadAsStringAsync();
            return JsonUtility.FromJson<Stuff>(json);
        }
        else
        {
            Debug.LogError($"Could not retrieve stuff {id}");
        }
    }
    catch (Exception exception)
    {
        Debug.LogError($"Exception when retrieving stuff {exception}");
    }
    return null;
}

나는 많은 기다림에 사용하고 있었기 때문에 응답을 받지 못했습니다. 나는 동기화 통화로 전환했습니다. 작동하기 시작했습니다.

            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage())
            {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                request.Method = HttpMethod.Get;
                request.RequestUri = new Uri(URL);
                var response = client.GetAsync(URL).Result;
                response.EnsureSuccessStatusCode();
                string responseBody = response.Content.ReadAsStringAsync().Result;
               

언급URL : https://stackoverflow.com/questions/10343632/httpclient-getasync-never-returns-when-using-await-async

반응형