Async/await と BackgroundWorker の質問

Async/await と BackgroundWorker の質問

ここ数日、.net 4.5 と c# 5 の新機能をテストしました。

新しいasync/await機能が気に入っています。以前はバックグラウンドワーカーレスポンシブな UI を使用して、バックグラウンドでより長いプロセスを処理します。

私の質問は、これらの素晴らしい新機能が利用可能になった後、async/awaitをいつ使用すべきか、そしてバックグラウンドワーカー? 両方に共通するシナリオは何ですか?

ベストアンサー1

これは多くの人にとって TL;DR かもしれませんが、比較することはリンゴとオレンジを比較するようなものだと私は考えており、awaitこれBackgroundWorkerについての私の考えは次のとおりです。

BackgroundWorkerは、スレッドプールのスレッド上でバックグラウンドで実行したい単一のタスクをモデル化することを目的としています。async/awaitは、非同期操作を非同期的に待機するための構文です。これらの操作は、スレッドプールのスレッドを使用する場合と使用しない場合があり、その他のスレッドつまり、それらはリンゴとオレンジなのです。

たとえば、次のようにすることができますawait

using (WebResponse response = await webReq.GetResponseAsync())
{
    using (Stream responseStream = response.GetResponseStream())
    {
        int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
    }
}

ただし、バックグラウンド ワーカーでこれをモデル化することはおそらくなく、.NET 4.0 (より前await) では次のような操作を行う可能性があります。

webReq.BeginGetResponse(ar =>
{
    WebResponse response = webReq.EndGetResponse(ar);
    Stream responseStream = response.GetResponseStream();
    responseStream.BeginRead(buffer, 0, buffer.Length, ar2 =>
    {
        int bytesRead = responseStream.EndRead(ar2);
        responseStream.Dispose();
        ((IDisposable) response).Dispose();
    }, null);
}, null);

2 つの構文を比較して、処理の不連続性と、/usingなしでは使用できないことに注意してください。asyncawait

しかし、 ではそのようなことは行いませんBackgroundWorkerBackgroundWorkerは通常、UI の応答性に影響を与えたくない単一の長時間実行操作をモデル化するために使用されます。 例:

worker.DoWork += (sender, e) =>
                    {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                    };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // TODO: do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

実際には、async/await を使用できるものは何もなく、BackgroundWorkerスレッドが自動的に作成されます。

代わりに TPL を使用することもできます。

var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
                      {
                        int i = 0;
                        // simulate lengthy operation
                        Stopwatch sw = Stopwatch.StartNew();
                        while (sw.Elapsed.TotalSeconds < 1)
                            ++i;
                      }).ContinueWith(t=>
                                      {
                                        // TODO: do something on the UI thread, like
                                        // update status or display "result"
                                      }, synchronizationContext);

この場合、 はTaskSchedulerスレッドを作成し (デフォルトの を想定TaskScheduler)、await次のように使用できます。

await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

私の意見では、主な比較は進捗状況を報告しているかどうかです。たとえば、BackgroundWorker like次のような場合です。

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
                            {
                            // TODO: something with progress, like update progress bar

                            };
worker.DoWork += (sender, e) =>
                 {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                            ((BackgroundWorker)sender).ReportProgress((int) (1000 / sw.ElapsedMilliseconds));
                        ++i;
                    }
                 };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

ただし、バックグラウンド ワーカー コンポーネントをフォームのデザイン サーフェイスにドラッグ アンド ドロップするため、このうちのいくつかは処理されません。これは、async/awaitおよび... では実行できないことです。つまり、オブジェクトを手動で作成したり、プロパティを設定したり、イベント ハンドラーを設定したりすることはありません。 、、およびイベント ハンドラーTaskの本体にのみ入力します。DoWorkRunWorkerCompletedProgressChanged

これを async/await に「変換」すると、次のようになります。

     IProgress<int> progress = new Progress<int>();

     progress.ProgressChanged += ( s, e ) =>
        {
           // TODO: do something with e.ProgressPercentage
           // like update progress bar
        };

     await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                        {
                            progress.Report((int) (1000 / sw.ElapsedMilliseconds))
                        }
                        ++i;
                    }
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

コンポーネントを Designer サーフェスにドラッグする機能がなければ、どちらが「優れている」かを判断するのは読者次第です。しかし、私にとって、それは と の比較でありawaitBackgroundWorkerのような組み込みメソッドを await できるかどうかではありませんStream.ReadAsync。たとえば、意図したとおりに を使用していた場合BackgroundWorker、 を使用するように変換するのは難しい場合がありますawait

他の考え:http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html

おすすめ記事