HttpClient에서 await를 사용한 비동기 호출이 반환되지 않음


95

C#Win8 CP 의 xaml 기반 메트로 애플리케이션 내부에서 전화를 겁니다 . 이 호출은 단순히 웹 서비스에 도달하고 JSON 데이터를 반환합니다.

HttpMessageHandler handler = new HttpClientHandler();

HttpClient httpClient = new HttpClient(handler);
httpClient.BaseAddress = new Uri("http://192.168.1.101/api/");

var result = await httpClient.GetStreamAsync("weeklyplan");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeeklyPlanData[]));
return (WeeklyPlanData[])ser.ReadObject(result);

멈춰 await있지만 http 호출은 실제로 거의 즉시 반환됩니다 (피들러를 통해 확인 됨). 마치 await무시되고 거기에 매달려 있습니다.

질문하기 전에 -예-사설 네트워크 기능이 켜져 있습니다.

왜 이것이 멈추는 지 아이디어가 있습니까?


1
그 방법을 어떻게 부르고 async있습니까? 예외가 발생하지 않습니까?
svick

답변:


136

매우 유사한 내 질문에 대한 답변 을 확인하십시오 .

시도 할 방법 : ConfigureAwait(false)에서 반환 한 Task를 호출 합니다 GetStreamAsync(). 예

var result = await httpClient.GetStreamAsync("weeklyplan")
                             .ConfigureAwait(continueOnCapturedContext:false);

이것이 유용한 지 여부는 위의 코드가 어떻게 호출되는지에 따라 다릅니다. 제 경우에는을 async사용 하여 메서드를 호출 Task.GetAwaiter().GetResult()하면 코드가 중단됩니다.

이는 GetResult()Task가 완료 될 때까지 현재 스레드를 차단 하기 때문 입니다. 작업이 완료되면 시작된 스레드 컨텍스트를 다시 입력하려고 시도하지만 해당 컨텍스트에 이미 스레드가 있으므로 GetResult()... 교착 상태 에 대한 호출에 의해 차단됩니다 .

이 MSDN 게시물 은 .NET이 병렬 스레드를 동기화하는 방법에 대해 약간 자세히 설명 하며 내 질문에 대한 답변 은 몇 가지 모범 사례를 제공합니다.


12
감사합니다. 이것을보기 전에 async / await를 거의 포기했습니다.
Den

4
나도! 이 자료가 더 잘 문서화되지 않은 이유는 무엇입니까? 다시 한 번 감사드립니다
Avrohom Yisroel 2014

1
UI 컨텍스트와 ASP.NET 컨텍스트 모두에 있지 않으면 발생합니까?
machinarium

1
멋진 대답입니다! 하지만 지금까지 HttpClient를 사용할 때만이 문제가 발생하는 이유가 혼란 스럽습니다. HttpClient 내의 기본 구현이 올바르게 구현되지 않은 것 같습니다. 내가 찾은 다른 해결 방법은 현재 스레드를 STA로 설정하는 것과 관련이 있지만 특히 타사 어셈블리를 사용하는 경우 특히 간접적이며 내부에서 일부 호출이 응답을 확실히 기다릴 것이라는 것을 알지 못합니다. 결코 얻을 수 없습니다. 제 경우에는 dll이 사내에 있었으므로 ConfigureAwait를 사용할 수 있었지만 HttpClient obj에 대한 최하위 수준에서 수행해야했습니다.
Chris Schaller

2
@ChrisSchaller 문제를 상당히 완전하게 설명하는 stackoverflow.com/a/10351400/174735 에서 전체 답변을 읽으십시오 .
Benjamin Fox

5

참고 사항-ASP.NET 컨트롤러의 최상위 수준에서 대기를 놓치고 응답으로 결과 대신 작업을 반환하면 실제로 중첩 된 대기 호출에서 오류없이 중단됩니다. 어리석은 실수이지만이 게시물을 봤다면 코드를 통해 이상한 것을 확인하는 데 시간을 절약 할 수 있었을 것입니다.


0

면책 조항 : 저는 ConfigureAwait () 솔루션이 직관적이지 않고 기억하기 어렵 기 때문에 마음에 들지 않습니다. 대신 Task.Run (() => myAsyncMethodNotUsingAwait ())에서 대기하지 않은 메서드 호출을 래핑하는 결론에 도달했습니다. 이것은 100 % 작동하는 것처럼 보이지만 경쟁 조건 일 수 있습니다!? 솔직히 무슨 일이 일어나고 있는지 잘 모르겠습니다. 이 결론은 잘못되었을 수 있으며 주석에서 배우기 위해 StackOverflow 포인트를 위험에 빠뜨릴 수 있습니다. 읽어주세요!

방금 설명한대로 문제가 발생했으며 여기 에서 더 많은 정보를 찾았 습니다 .

문은 "비동기 메서드를 호출 할 수 없습니다"입니다.

await asyncmethod2()

차단하는 방법에서

myAsyncMethod().Result

제 경우에는 호출 방법을 변경할 수 없었고 비동기가 아닙니다. 하지만 실제로 결과에 대해서는 신경 쓰지 않았습니다. 내가 기억하기 때문에 .Result를 제거하고 await가 누락되었습니다.

그래서 이렇게했습니다.

public void Configure()
{
    var data = "my data";
    Task.Run(() => NotifyApi(data));
}

private async Task NotifyApi(bool data)
{
    var toSend = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
    await client.PostAsync("http://...", data);
}

제 경우에는 비동기 메서드 호출의 결과에 대해 신경 쓰지 않았지만이 사용 사례에서는 매우 일반적이라고 생각합니다. 호출하는 비동기 메서드에서 결과를 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.