2017-12-12 9 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

それが終わるのを待たずにいたいなら、これは本質的には忘れ去られるシナリオです。これは、Web 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

asyncは、topからbotomまでのすべての方法で実装する必要があります。

WebApiコントローラのように、非同期的にスタックを上回るメソッドを実行するとどうなりますか?

ただ、このようなあなたのコントローラのアクション非同期を行います

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

これは、非同期メソッドを実装する最も簡単な方法です。非同期コードへの多くの問題を避けるため、すべてを非同期に変更する作業を追加しても、将来的には有効です。

あなたはabsolutly同期メソッドで非同期メソッドを使用する必要がある場合、それはこのように、一つの場所にブロックします

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

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

しかし、私はそれをお勧めしません。あなたはただ非同期を使用するだけで、コントローラの操作を待っています。

+2

この例は、 'ConfigureAwait(false)'を使用しない限り、ASP.NETでデッドロックします( 'await 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.Currentは、PushResultの内部では使用できません。

+0

'ConfigureAwait'はあなたが実際にそれを待っていないときは無意味です。あなたが決して実行しないことを待つことを構成することは、何も達成していない。 – Servy

+0

あなたはまた、OPが本質的に非同期メソッドを使用することを主張しています。メソッドの結果をブロックしてから、「Task.Run」を使用してスレッドプールスレッドでsync-over-asyncラッパーを開始します。それはすべての種類の混乱し、非生産的です。 – Servy

+0

スレッドをブロックします(タスクがとにかく終了するまで)が、OPが探しているものである要求をブロックしません。私はWeb APIで以前は忘れていた仕事を使いました。それには危険性がありますが、その場所もあります。 'ConfigureAwait'は重要です。私はそれがなければ、タスクが終了するまでレスポンスが返ってこないことを発見しました。これは火災と忘れられないものです。 –

関連する問題