16

私はc#5の非同期機能を初めて使用します。私はこれら2つの実装の違いを理解しようとしています。

実装1:

private void Start()
{
    foreach(var url in urls)
    {
        ParseHtml(url);
    }
}

private async void ParseHtml(string url)
{
    var query = BuildQuery(url); //BuildQuery is some helper method
    var html = await DownloadHtml(query);
    //...
    MyType parsedItem = ParseHtml(html);
    SaveTypeToDB(parsedItem);
}

private async Task<string> DownloadHtml(string query)
{
    using (var client = new HttpClient())
    try
    {
        var response = await client.GetAsync(query);
        return (await response.Content.ReadAsAsync<string>());
    }
    catch (Exception ex)
    {
        Logger.Error(msg, ex);
        return null;
    }
}

実装2:

private void DoLoop()
{
    foreach(var url in urls)
    {
        Start(url);
    }
}

private async void Start(url)
{
    await Task.Run( () => ParseHtml(url)) ;
}

private void ParseHtml(string url)
{
    var query = BuildQuery(url); //BuildQuery is some helper method
    var html = DownloadHtml(query);
    //...
    MyType parsedItem = ParseHtml(html);
    SaveTypeToDB(parsedItem);
}

private string DownloadHtml(string query)
{
    using (var client = new WebClient())
    {
        try
        {
            return client.DownloadString(query);
        }
        catch (Exception ex)
        {
            Logger.Error(msg, ex);
            return null;
        }
    }
}

コード内のメソッドに必要な「非同期」シグネチャが少なくなるため、2番目の実装を使用したいと思います。HttpClientクラスを使用することと、新しいタスクを使用してそれを待つことの利点を理解しようとしていますか?

2つの実装に違いはありますか?

4

3 に答える 3

29

コード内のメソッドに必要な「非同期」シグネチャが少なくなるため、2番目の実装を使用したいと思います。

それは非常に奇妙な正当化のように聞こえます。基本的に「ある程度非同期」で実行しようとしているので、それを明確にしてみませんか?

2つの実装に違いはありますか?

絶対。2番目の実装は、ブロック中にスレッドをWebClient.DownloadString拘束し、要求が完了するのを待ちます。最初のバージョンにはブロックされたスレッドがありません-リクエストが終了したときに起動し続けることに依存しています。

さらに、あなたのLogger.Error電話を考慮してください。非同期バージョンでは、元の呼び出しコードのコンテキストで実行されます。したがって、これがたとえばWindowsフォームUIにある場合は、引き続きUIスレッドを使用し、UI要素などにアクセスできます。2番目のバージョンでは、スレッドプールスレッドで実行するため、次のことが必要になります。 UIスレッドにマーシャリングしてUIを更新します。

async voidあなたの方法はほぼ確実にすべきではないことに注意してくださいasync void。イベントハンドラのシグネチャに準拠するためにのみ、asyncメソッドを返す必要があります。void他のすべての場合は、戻り値を返しTaskます。これにより、呼び出し元はタスクが終了したことを確認したり、例外を処理したりできます。

HttpClientまた、非同期に使用する必要はないことに注意してくださいWebClient.DownloadStringTaskAsync。代わりに使用できるため、最終的なメソッドは次のようになります。

private async Task<string> DownloadHtmlAsync(string query)
{
    using (var client = new WebClient())
    {
        try
        {
            return await client.DownloadStringTaskAsync(query);
        }
        catch (Exception ex)
        {
            Logger.Error(msg, ex);
            return null;
        }
    }
}
于 2012-11-23T22:59:06.060 に答える
6

サーバーアプリケーションの場合async、ブロックされるスレッドの数を最小限に抑えることです。スレッドプールの効率を高め、プログラムをより多くのユーザーに拡張できるようにすることです。

スレッド数を気にする必要がないクライアントアプリケーションasyncの場合、I/Oを実行するときにUIをスムーズに実行し続けるための比較的簡単な方法を提供します。

Task.Runボンネットの下とは大きく異なります。

于 2012-11-24T17:27:25.693 に答える
1

UIの応答性を維持するなど、呼び出し元のスレッドに意味のあることがある場合にのみ、非同期処理のメリットが得られます。呼び出し元のスレッドがタスクを開始するだけで、タスクが終了するまで待つだけの場合、プロセスの実行速度は遅くなります。

2番目の実装はタスクを開始しますが、他に何もせずにタスクが完了するのを待ちます。このようにあなたは利益を得ません。

環境については説明しませんが、レスポンシブを維持する必要のあるUIがある場合は、Start()が非同期として宣言されておらず、待機しないことを除いて、メソッド1の実装は問題ありません。

private async Task StartAsync()
{
    foreach (var url in urls)
    {
        await ParseHtml(url)
    }
}

これは、次のようにイベントハンドラーから呼び出すことができます。

private async void OnButton1_clicked(object sender, ...)
{
   await StartAsync();
}

注:ParseHtmlの前にはawaitがあります。次のhtmlは、前の解析が終了した後に解析されます。ただし、解析は非同期であるため、呼び出し元のスレッド(UIスレッド?)は、ユーザー入力への応答などの他のことを実行できます。

ただし、parseHTML関数を同時に実行できる場合は、次のコードが望ましく、おそらくより高速です。

private async Task StartAsync()
{
    var tasks = new List<Task>()
    foreach (var url in urls)
    {
        tasks.Add(ParseHtml(url));
    }
    // now you have a sequence of Tasks scheduled to be run, possibly simultaneously.
    // you can do some other processing here
    // once you need to be certain that all tasks are finished await Task.WhenAll(...)
    await Task.WhenAll(tasks);
    // Task.WhenAls(...) returns a Task, hence you can await for it
    // the return of the await is a void
}
  • タスクを返す関数を呼び出すと、タスクの実行中に他のことを続行したり、タスクが終了するのを待ってタスクの結果を使用したりできます。
  • 待つとコードは停止しますが、発信者はタスクが完了するのを待つまで処理を続行します
  • プロシージャが非同期の場合にのみ、プロシージャで待機できます。
  • 非同期関数は、他の非同期関数、またはTask.Run(()=> ...)を呼び出すか、必要に応じてTask.Factory.StartNew(()=> ...)を呼び出すことによってのみ呼び出すことができます。
  • voidの代わりに、非同期関数はTaskを返します
  • TResultの代わりに、非同期関数はTask <TResult>を返します
  • 唯一の例外はイベントハンドラーです。非同期として宣言し、voidを返します。
  • タスクを完了する必要がある場合は、タスクを待つだけです。
  • 待機の戻りはTResultです。
于 2015-08-10T11:53:06.590 に答える