19

2 種類の C# WPF アプリ プロジェクトがあります。

  • .NET 4.5に移行できない.NET 4.0に基づいています
  • .NET 4.5に移行できる.NET 4.0に基づいています

それらはすべて、ユーザーがキャンセルして再起動できる 2 ~ 10 日間の長時間実行されるプロセスを生成する必要があります。

ベスト デザイン プラクティスに従うことに関心があります。まず第一に、BackgroundWorker使用法について明確にすることに興味がありますが、私の質問が他の非同期パターンについて有効であることを願っています。

私は(矛盾する)同時並行的な視点を見る

非同期パターン:

「非同期プログラミングへの非同期ベースのアプローチは、ほぼすべてのケースで既存のアプローチよりも優れています。特に、このアプローチは、IO バウンド操作のBackgroundWorkerよりも優れています。これは、コードが単純であり、競合状態を防ぐ必要がないためです。 Task.Run と組み合わせると、非同期プログラミングは 、コード実行の調整の詳細を、Task.Run がスレッドプールに転送する作業から分離するため、CPU バウンド操作で はBackgroundWorkerよりも優れています。"

私はまだ疑問があります:

  1. これらのパターン (まず第一に、BGW) は .NET 4.5 で廃止されましたか?
  2. それらが .NET 4.5 で廃止された場合、.NET 4.0 で廃止されないのはなぜですか?

    2A) .NET 4.5 の新機能は .NET 4.0 でも「簡単に」実装/再現可能であると誤解していますか?

4

3 に答える 3

15

.NET 4.5 を使用している場合は、一般的にTaskand/orをお勧めします。awaitしかし、TaskBGW には 2 つの明確に異なるシナリオがあります。Task は、継続に連鎖できる一般的な短い非同期タスクに適しており、await は暗黙的に UI スレッドにマーシャリングするタスクに適しています。BGW は、UI の応答性に影響を与えない単一の長い操作に適しています。BGW をデザイン サーフェイスにドラッグ アンド ドロップし、ダブルクリックしてイベント ハンドラーを作成できます。別のスレッドにマーシャリングしたくない場合LongRunningは、対処する必要はありません。ConfigureAwait多くの人は、BGW の進行は よりも簡単だと感じていますIProgress<T>

「長時間の操作」シナリオで両方を使用する例を次に示します。

Task質問は特に .NET 4.0 に言及しているため、UI に進行状況を提供しながら長い操作を実行するためにa を使用する単純なコードを次に示します。

startButton.Enabled = false;
var task = Task.Factory.
                StartNew(() =>
                    {
                        foreach (var x in Enumerable.Range(1, 10))
                        {
                            var progress = x*10;
                            Thread.Sleep(500); // fake work
                            BeginInvoke((Action) delegate { 
                               progressBar1.Value = progress; 
                            });
                        }
                    }, TaskCreationOptions.LongRunning)
                .ContinueWith(t =>
                    {
                        startButton.Enabled = true;
                        progressBar1.Value = 0;
                    });

同様のコードは次のBackgroundWorkerようになります。

startButton.Enabled = false;
BackgroundWorker bgw = new BackgroundWorker { WorkerReportsProgress = true };
bgw.ProgressChanged += (sender, args) => 
    { progressBar1.Value = args.ProgressPercentage; };
bgw.RunWorkerCompleted += (sender, args) =>
{
    startButton.Enabled = true;
    progressBar1.Value = 0;
};
bgw.DoWork += (sender, args) =>
{
    foreach (var x in Enumerable.Range(1, 10))
    {
        Thread.Sleep(500);
        ((BackgroundWorker)sender).ReportProgress(x * 10);
    }
};
bgw.RunWorkerAsync();

.NET 4.5 を使用している場合は、.NET呼び出しProgress<T>の代わりに を使用できます。4.5 以降では、次のようにするとより読みやすくなります。BeginInvokeTaskawait

startButton.Enabled = false;
var pr = new Progress<int>();
pr.ProgressChanged += (o, i) => progressBar1.Value = i;
await Task.Factory.
            StartNew(() =>
                        {
                            foreach (var x in Enumerable.Range(1, 10))
                            {
                                Thread.Sleep(500); // fake work
                                ((IProgress<int>) pr).Report(x*10);
                            }
                        }, TaskCreationOptions.LongRunning);
startButton.Enabled = true;
progressBar1.Value = 0;

UsingProgress<T>は、コードが特定の UI フレームワーク (つまり の呼び出し) に結合されていないことを意味します。これは、特定の UI フレームワークからの切り離しを容易にするBeginInvokeのとほぼ同じ方法です。BackgroundWorker気にしない場合は、使用の複雑さを追加する必要はありませんProgress<T>

に関してはLongRunning、Stephen Toub が言うように、「LongRunning を使用しないと他の作業の処理に長い遅延が発生することがパフォーマンス テストでわかった場合にのみ、LongRunning を使用するのが一般的です」。それを使用してください-追加の分析または常にパラメーターを追加することの「複雑さ」がありLongRunningます。LongRunning を使用しないということは、実行時間の長い操作に使用されるスレッド プール スレッドが他のより一時的なタスクに使用できなくなり、スレッド プールがこれらの一時的なタスクの 1 つの開始を遅らせ、別のスレッドを開始することを意味します (少なくとも2番目)。

フレームワークには、BGW (または EAP、または APM) が非推奨であることを明確に示す属性はありません。したがって、これらのものがいつどこで「時代遅れ」になるかを決めるのはあなた次第です。特に BGW には、依然として適用される非常に具体的な使用シナリオが常にありました。.NET 4.0 と 4.5 にはかなりまともな代替手段があります。しかし、BGWが「時代遅れ」だとは本当に思いません。

BackgroundWorker常に useと言っているわけではありません。BackgroundWorker を自動的に非推奨にする前に考えてください。場合によっては、それがより良い選択かもしれません。

于 2013-05-04T14:19:28.260 に答える
15

これらのパターン (特に APM、EAP、および BGW) は、.NET 4.5 では廃止されたと考えています。との組み合わせはasyncTask.Runあらゆる点で BGW よりも優れています。実際、私は自分のブログで BGW と比較しTask.Run、あらゆる状況で BGW がいかに扱いにくいかを示すシリーズを始めたところです。少しだけ面倒な状況もあれば、もっと面倒な状況もあります

さて、それらが .NET 4.0 で廃止されるかどうかは、まったく別の問題です。他の投稿から、 VS2010を使用した .NET 4.0 の開発について話しているため、バックポートMicrosoft.Bcl.Asyncはオプションではありません。その場合、APM も EAP も時代遅れの IMO と見なすことはできません。このプラットフォームでは、BGW の代替と考えることができますがTask.Factory.StartNew、BGW には、進行状況のレポートと、進行状況および完了イベントの自動スレッド マーシャリングに関して、いくつかの利点があります。

更新:私は最近、バックグラウンド操作のさまざまな実装について説明している私の古いブログ投稿を更新しました。その投稿で、「タスク (非同期メソッド)」について話すときはTask、すべての .NET 4.5asyncサポートTask.Runなどを使用することを意味します。「タスク (タスク並列ライブラリ)」セクションはTask、.NET 4.0 に存在していたように評価しています。

于 2013-05-03T17:09:15.437 に答える
1

私の質問は:

APM、EAP、BackgroundWorker の非同期パターンを廃止したのは .NET 4.0 TPL ではありませんか?

async/awaitつまり、何かが .NET 4.5 で廃止された場合、それが .NET 4.0 で、つまり Task Parallel Library を使用して (エンゲージメントと .NET 4.5 のみのサポートなしで) 異なる理由と方法がわからないという確認または否定を求める疑問です。)

そして、コードプロジェクトの記事「Task Parallel Library and async-await Functionality - Patterns of Usage in Easy Samples」でNick Polyak の回答を見つけることができて、非常に嬉しく思います。

  • 「.NET 4.5 の機能の多くは Tasks に変更され、BackgroundWorker 現在はほとんど廃止されていますが、EAP パターンを使用する必要がある場合に、まだいくつかのインスタンスに遭遇する可能性があります。そのような場合、Task オブジェクトを生成すると便利です。これにより、それらを並行して実行するようにスケジュールしたり、以前のいくつかのタスクが完了するのを待つことができます。このセクションでは、それを実現する方法を示します。"
  • 「Task 機能を持つことで、機能がBackgroundWorker大幅に時代遅れになりました。必要なものは何でも Task の代わりに BackgroundWorker を使用して達成できます。それでも、チームで機能を使用したい理由があるかもしれませんBackgroundWorker- レガシー コードがそれを使用しているからか、ほとんどの場合、チーム メンバーや上司はそれを気に入っており、新しいタスク機能よりもよく理解しています。」

異なる議論の視点を見るためにまだ開かれている

于 2013-05-05T11:49:24.893 に答える