2016-04-01 45 views
15

私は以下の方法があります:非同期メソッドから非同期メソッドを呼び出す方法

public string RetrieveHolidayDatesFromSource() { 
     var result = this.RetrieveHolidayDatesFromSourceAsync(); 
     /** Do stuff **/ 
     var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/ 
     return returnedResult; 
    } 


    private async Task<string> RetrieveHolidayDatesFromSourceAsync() { 
     using (var httpClient = new HttpClient()) { 
      var json = await httpClient.GetStringAsync(SourceURI); 
      return json; 
     } 
    } 

上記の作業と適切に結果を返しませいるようだありません。私は結果を待つ声明がどこにないのか分からないのですか? RetrieveHolidayDatesFromSource()メソッドが文字列を返すようにします。

以下はうまく動作しますが、同期していて、改善できると思いますか?以下は同期ですが、私は非同期に変更したいのですが、何らかの理由で頭を包み込むことができません。

public string RetrieveHolidayDatesFromSource() { 
     var result = this.RetrieveHolidayDatesFromSourceAsync(); 
     /** Do Stuff **/ 

     var returnedResult = this.TransformResults(result); /** This is where Result is actually used**/ 
     return returnedResult; 
    } 


    private string RetrieveHolidayDatesFromSourceAsync() { 
     using (var httpClient = new HttpClient()) { 
      var json = httpClient.GetStringAsync(SourceURI); 
      return json.Result; 
     } 
    } 

何か不足していますか?

注:上記の非同期メソッドをブレークポイントすると、 "var json = await httpClient.GetStringAsync(SourceURI)"という行に達したときに何らかの理由でブレークポイントから外れてしまい、メソッド。

答えて

23

何か不足していますか?

はい。非同期コードは、その性質上、操作の進行中に現在のスレッドが使用されていないことを意味します。同期コードは、その性質上、操作の進行中に現在のスレッドがブロックされていることを意味します。このため、同期コードから非同期コードを呼び出すことは、文字通り意味をなさないことです。実際、私のブログで説明しているように、a naive approach (using Result/Wait) can easily result in deadlocks

最初に考慮する点は次のとおりです。should my APIは同期または非同期である必要がありますか? I/Oを扱う場合(この例のように)、それはshould be asynchronousです。だから、これはより適切な設計のようになります。

public async Task<string> RetrieveHolidayDatesFromSourceAsync() { 
    var result = await this.DoRetrieveHolidayDatesFromSourceAsync(); 
    /** Do stuff **/ 
    var returnedResult = this.TransformResults(result); /** Where result gets used **/ 
    return returnedResult; 
} 

私は私のasync best practices articleで説明したよう、あなたは「すべての道非同期(async)」に行く必要があります。あなたがしない場合は、とにかく非同期の利点を得ることはありません、なぜそんなに気に?

しかし、あなたは非同期に行く結局に興味を持っている、しかし、今あなたはあなたは自分のアプリの一部を変更したい、すべてを変更することはできませんと言ってみましょう。それはかなり一般的な状況です。

この場合、適切な方法は同期APIと非同期APIの両方を公開することです。最終的に、他のすべてのコードがアップグレードされた後、同期APIを削除することができます。私はarticle on brownfield async developmentのこの種のシナリオのための様々なオプションを探る。

public string RetrieveHolidayDatesFromSource() { 
    return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult(); 
} 

public Task<string> RetrieveHolidayDatesFromSourceAsync() { 
    return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false); 
} 

private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) { 
    var result = await this.GetHolidayDatesAsync(sync); 
    /** Do stuff **/ 
    var returnedResult = this.TransformResults(result); 
    return returnedResult; 
} 

private async Task<string> GetHolidayDatesAsync(bool sync) { 
    using (var client = new WebClient()) { 
    return sync 
     ? client.DownloadString(SourceURI) 
     : await client.DownloadStringTaskAsync(SourceURI); 
    } 
} 

このアプローチでは、コードの重複を回避し、また、他の「同期オーバー非同期」アンチパターンソリューションと共通の任意のデッドロックまたは再入の問題を回避:私の個人的な好みは次のようになり、「ブールパラメータハック」、です。

私はまだ結果のコードを、適切に非同期のAPIへのパス上の「中間ステップ」として扱うことに注意してください。特に、内部コードは、HttpClient(非同期のみをサポート)の代わりにWebClient(同期と非同期の両方をサポート)に戻らなければなりませんでした。すべてのコーリングコードがでなくを使用するように変更されたら、私はこれを再訪し、すべての技術的負債を取り除き、HttpClientを使用するように変更し、非同期のみにします。

+1

'publicタスク RetrieveHolidayDatesFromSourceAsync()'に 'await'がありませんか? –

+3

@NickWeaver:いいえ、非同期ではないので、 'await'を使うことはできません。 –

+0

私は参照してください。メソッドの名前が最後に "Async"と書かれていたので、私は疑問に思っていました。なぜ非同期に設定されていないのですか? –

2
public string RetrieveHolidayDatesFromSource() { 
    var result = this.RetrieveHolidayDatesFromSourceAsync().Result; 
    /** Do stuff **/ 
    var returnedResult = this.TransformResults(result.Result); /** Where result gets used **/ 
    return returnedResult; 
} 

同期することを強制的に、あなたは非同期呼び出しに.Resultを追加した場合、それが実行され、結果が到着するのを待つ

UPDATE:

private static string stringTest() 
{ 
    return getStringAsync().Result; 
} 

private static async Task<string> getStringAsync() 
{ 
    return await Task.FromResult<string>("Hello"); 
} 
static void Main(string[] args) 
{ 
    Console.WriteLine(stringTest()); 

} 

コメントに対処する:これは問題なく動作します。

+1

私はすでにメソッド内に "result.Result"があるので、これは動作していないと思います。 thisを返します.RetrieveHolidayDatesFromSourceAsync()。resultは結果を非タスクオブジェクトに変更し、 "var returnedResult = this.TransformResults(result.Result)"にエラーを発生させます。 –

+0

作業コードを表示するために私の答えが更新されました。 getStringAsyncが同期メソッド(stringTest())で呼び出される非同期メソッドであることがわかります。エラーなしで目的の出力を取得します。あなたは何を得ているのですか? –

+4

「これは何の問題もなく動作します」:...)あなたはちょっとおいしいかもしれませんし、コンソールアプリケーションの外で実際に使用されたときのデッドロックの対処方法について説明します。 –

関連する問題