2017-12-12 4 views
0

저는 2 일 동안 벽에 머리를 대고 있었고, 솔직히 말해서 나 자신에게 짜증이났습니다. .동기식으로 비동기 통합

나는 webapi 컨텍스트에 있습니다. 이 요청을하는 동안 나는 다른 시스템 중 하나에 데이터를 보내야합니다.이 시스템은 계산이 복잡하고 데이터베이스 저장 등으로 인해 돌아 오는 것이 느립니다. 성공 여부에 관계없이이 작업의 결과를 기록해야합니다. 아닙니다. 하지만 끝내기 위해 기다릴 필요가 없습니다.

read 나는 위에서 아래로 끝까지 async await이어야합니다. 내가 이미 3 ~ 4 가지 방법으로 깊이있는이 방법을 사용하기로 결정했다면 수많은 방법으로 변환해야했습니다.

내 옵션에는 어떤 것들이 있습니까? 비동기가 끝나면 WebApi 컨트롤러처럼 스택 위로 올라가는 방법으로 무엇을해야합니까?

여기 내 코드입니다. 최대한 줄일 수 있도록 노력했습니다. 지금은 PushResult() 메서드에서 Task.Result()을 사용하고 있습니다. 비동기를 막는 것이 나의 이해에 어느 것입니까? 이 코드는 요청이 전송된다는 점에서 작동합니다. 그러나 TestLog는 항상 최후가 아닌 최후입니다. 따라서 비동기가 아닙니다.

//I'm in a public service and referenced twice 
    private void MyEndProcess() 
    { 
     // other stuff 

     _vendorPushService.PushResult(); // This could take a while and I have to wait for it! 

     _logService.PostLog(LogType.TestLog, "Test"); 
    } 

    //I'm referenced above and somewhere else in the code base 
    public void PushResult() 
    { 
     ExternalResultModel externalResultModel = _resultService.GetExternalResultModel(); 

     PushedResultModel pushedResult = new PushedResultModel(); 

     try 
     { 
      pushedResult = _vendorRequestService.PushResultAsync(externalResultModel).Result; 
     } 
     catch (Exception ex) 
     { 
      pushedResult.Success = false; 
     } 

     if (pushedResult.Success) 
     { 
      _logService.PostLog(LogType.SuccessLog, pushedResult.Message); 
     } 
     else 
     { 
      _logService.PostLog(LogType.FailedLog, pushedResult.Message); 
     } 
    } 

    public async Task<PushedResultModel> PushResultAsync(ExternalResultModel externalResultModel) 
    { 
     // setup the requestMessage 
     HttpResponseMessage responseMessage = await _httpRequestService 
      .SendRequest(requestMessage) 
      .ConfigureAwait(false); 

     return new PushedResultModel 
     { 
      Success = responseMessage.IsSuccessStatusCode, 
      Message = await responseMessage.Content.ReadAsStringAsync() 
     }; 
    } 

    public class HttpRequestService : IHttpRequestService 
    { 
     private readonly HttpClient _httpClient; 

     public HttpRequestService(IHttpClientAccessor httpClientAccessor) 
     { 
      _httpClient = httpClientAccessor.HttpClient; 
     } 

     public async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage) 
     { 
      HttpResponseMessage httpResponseMessage = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false); 

      return httpResponseMessage; 
     } 
    } 
+3

모든 방법을 비동기로 사용하지 않으려는 경우 모든 항목을 동기 상태로두고 비동기 코드를 입력하지 마십시오. 전체 호출 스택을 비동기로 만들지 않고 비동기 코드를 추가하여 * 문제 *를 일으킬뿐입니다. – Servy

+2

끝내기를 기다리지 않으려면 본질적으로 불타고 잊어 버릴 시나리오입니다. 이것은 웹 API 컨텍스트에서 자신의 문제가 있습니다. 여기를 보길 권합니다. http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html. 실제 질문에 답하기 위해 [ContinueWith'] (https://msdn.microsoft.com/en-us/library/dd321405(v=vs.110) .aspx)를보고 싶을 수도 있습니다. 스티븐의 글을 먼저 읽으십시오. – bornfromanegg

+2

백그라운드 작업을 시작하고 클라이언트가 클라이언트에 응답을 보내기 전에 완료 될 때까지 기다리지 않아야하는 경우 - async await은별로 도움이되지 않습니다. – Evk

답변

0

비동기를 구현하려면 먼저 비동기를 구현해야합니다.

비동기가 끝나면, WebApi 컨트롤러처럼 스택 위로 높은 방법으로 무엇을해야합니까?

그냥 같이 컨트롤러 액션 비동기합니다

[RoutePrefix("api")] 
public class PresidentsController : ApiController 
{ 
    [Route("presidents")] 
    public async Task<IHttpActionResult> GetPresidents() 
    { 
     await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false); 
     return Ok(); 
    } 
} 

는 비동기 메소드를 구현하는 가장 쉬운 방법입니다. 비동기 코드에 대한 많은 문제를 피할 것이므로 비동기로 모든 것을 변경하는 작업을 추가하더라도 나중에 도움이 될 것입니다.

public void MySyncMethod() 
    { 
     try 
     { 
      this.MyAsyncMethod().Wait(); 

     } 
     catch (Exception exception) 
     { 
      //omited 
     } 
    } 
private async Task MyAsyncMethod() 
{ 
    await AsyncLogic().ConfigureAwait(false); 
} 

하지만 난 그것을 권 해드립니다하지 않습니다

당신이 절대적으로 동기 방법에 비동기 방식을 사용해야하는 경우

는 다음과 같이, 그것은 한 곳에서 차단합니다. 컨트롤러 동작을 위해 비동기를 사용하면됩니다.

+2

에서 화재 및 잊어 버린 작업에 대한 위의 링크를 읽으십시오.이 예는'ConfigureAwait (false)'를 사용하지 않는 한 ASP.NET에서 교착 상태가됩니다 (즉, AsyncLogic(), ConfigureAwait (false) 대기). https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – bornfromanegg

+0

네 말이 맞아, 그걸 잊어 버렸어, 네. 웃긴 점은 localhost에서 작동한다는 것입니다. 예제가 편집되었습니다. – garret

-1

귀하의 의견에 당신은 백그라운드에서 작업을 처리하고 클라이언트가 API를 기다리는 것을 기다리지 말라고 말했습니다. 그렇게하기 위해 async/await를 사용할 필요가 없습니다.

이 시도 :

private void MyEndProcess() 
{ 
    // other stuff 

    Task.Run(_vendorPushService.PushResult()).ConfigureAwait(false); //fire and forget 

    _logService.PostLog(LogType.TestLog, "Test"); 
} 

Task.Run가 작업을 시작하고, ConfigureAwait(false) 그것이 우리가 현재하고있는 같은 상황에 재개 할 필요가 없다는를 알려줍니다 (문맥 닫을 수 있다는 것을 의미 작업이 끝나기 전에 응답을 되돌릴 수 있습니다.

당신은 Task.Run을 기다리지 않고있다라고 경고하는 컴파일러를 얻지 만, 그것이 당신이 원하는 것입니다.

HttpContext.CurrentPushResult에서 사용할 수 없습니다.

+0

'ConfigureAwait'는 실제로 기다리지 않을 때 무의미합니다. 당신이 결코 수행하지 않을 것을 기다리는 구성은 아무 것도 성취하지 못합니다. – Servy

+0

또한 OP가 본질적으로 비동기 메서드를 사용하도록 권유하고 메서드에서 결과를 차단 한 다음 'Task.Run'을 사용하여 스레드 풀 스레드에서 sync-over-async 래퍼를 시작합니다. 그것은 모든 종류의 엉망이고 비생산적입니다. – Servy

+0

스레드를 차단합니다 (작업이 완료 될 때까지). 요청을 차단하지는 않습니다. 즉 OP가 찾고있는 요청입니다. 이전에는 웹 API에서 불과 잊어 버린 작업을 사용했습니다. 그것은 위험을 가지고 있지만 그것도 그 자리를 가지고 있습니다. 'ConfigureAwait'는 중요 하지요. 태스크가 끝날 때까지 응답이 되돌아 오지 않는다는 것을 발견했습니다. 그것은 화재와 잊지가 아닙니다. –

관련 문제