12

デリゲートは、.NETリファレンスでスレッド化を容易にするオブジェクトの一部です。メソッドを非同期的に呼び出すために使用できます。フレームワーク 4.5 (またはそれ以前) には、スレッドの使用を容易にする、またはエラーを起こしにくくする他のオブジェクトはありますか?

並行性とマルチスレッド化を容易にする他の抽象化は何ですか?

注: この質問はこれを更新します

4

4 に答える 4

21

私はマルチスレッドに関連する多くの質問に答える傾向があり、同じ基本的な質問がさまざまな方法で尋ねられることがよくあります。ここでは、私が何年にもわたって目にしてきた最も一般的な問題を紹介し、新しいテクノロジによってこれらの問題の解決がいかに容易になったかを説明します。

ループ変数を閉じる

これはスレッド化に固有の問題ではありませんが、スレッド化を使用すると問題が大きくなります。foreachC# 5.0では、反復ごとに新しい変数を作成することで、ループに関するこの問題が修正されています。ラムダ式クロージャー用の特別な変数を作成する必要がなくなります。残念ながら、forループは特別なキャプチャ変数で処理する必要があります。

非同期タスクの完了を待機しています

.NET 4.0 では、CountdownEvent多くのタスクの完了を待機するために必要な多くのロジックをカプセル化するクラスが導入されました。ほとんどのジュニア開発者はThread.Join、通話または単一のWaitHandle.WaitAll通話を使用しました。どちらもスケーラビリティの問題があります。古いパターンでは、シングルを使用しManualResetEvent、カウンターがゼロに達したときに信号を送っていました。カウンターは、Interlockedクラスを使用して更新されました。CountdownEventこのパターンをはるかに簡単にします。すべてのワーカーがキューに入れられる前に 1 つのワーカーが終了した場合に発生する微妙な競合状態を回避するために、メインもワーカーとして扱うことを忘れないでください。

.NET 4.0 では、.NETTaskを介して子タスクを連鎖させることができるクラスも導入されましたTaskCreationOptions.AttachedToParentTask.Wait親を呼び出すと、すべての子タスクも完了するまで待機します。

生産者と消費者

.NET 4.0 ではBlockingCollection、コレクションが空のときにブロックできることを除いて、通常のキューのように機能するクラスが導入されました。を呼び出してオブジェクトをキューに入れたり、 を呼び出しAddてオブジェクトをデキューしたりできますTakeTakeアイテムが利用可能になるまでブロックします。これにより、生産者と消費者のロジックが大幅に簡素化されます。以前は、開発者が独自のブロッキング キュー クラスを記述しようとしていたケースがありました。しかし、自分が何をしているのかわからなければ、本当に失敗する可能性があります...まずいです。実際、Microsoft は長い間、MSDN のドキュメントにブロッキング キューの例を掲載していましたが、それ自体がひどく壊れていました。幸いなことに、それはその後削除されました。

ワーカー スレッドの進行状況で UI を更新する

の導入によりBackgroundWorker、初心者の開発者にとって、WinForm アプリケーションからのバックグラウンド タスクのスピンオフがはるかに簡単になりました。主な利点は、イベント ハンドラーReportProgress内から呼び出すことができ、イベント ハンドラーが UI スレッドに自動的にマーシャリングされることです。もちろん、SO に関する私の回答を追跡している人なら誰でも、単純な進行状況情報で UI を更新するためのソリューションとして、マーシャリング操作 (経由など) について私がどのように感じているかを知っています。それは一般的にひどいアプローチなので、私はいつもそれを破っています。開発者は引き続き (バックグラウンドでのマーシャリング操作を介して) プッシュ モデルに強制されますが、少なくともこれはすべて舞台裏で行われます。DoWorkProgressChangedInvokeBackgroundWorker

Invoke の優雅さ

UI 要素には UI スレッドからのみアクセスできることは誰もが知っています。ISynchronizeInvokeこれは一般に、開発者が、DispatcherObject、またはを介し​​てマーシャリング操作を使用して、SynchronizationContext制御を UI スレッドに戻す必要があることを意味していました。しかし、それに直面しましょう。これらのマーシャリング操作は醜く見えます。Task.ContinueWithこれをもう少しエレガントにしましたが、真の栄光はawaitC# 5 の新しい非同期プログラミング モデルの一部として発揮されます。を使用して、タスクの実行中にフロー制御が一時的に中断され、適切な同期コンテキストのまさにその時点で返されるような方法で が完了するawaitのを待つことができます。これらすべての呼び出しの代わりとしてTask使用することほど、エレガントで満足のいくものはありません。awaitInvoke

並列プログラミング

どうすれば並列処理できるのかという質問をよく見かけます。古い方法では、いくつかのスレッドを作成するか、ThreadPool. .NET 4.0 では、TPL と PLINQ が使用されました。このParallelクラスは、ループの反復を並行して行うための優れた方法です。そして、PLINQAsParallelは単純な古い LINQ の同じコインの別の側面です。これらの新しい TPL 機能により、このカテゴリのマルチスレッド プログラミングが大幅に簡素化されます。

.NET 4.5 では、TPL データ フロー ライブラリが導入されています。複雑な並列プログラミングの問題をエレガントにすることを目的としています。クラスをブロックに抽象化します。それらは、ターゲット ブロックまたはソース ブロックになります。データは、あるブロックから別のブロックに流れることができます。BufferBlock<T>BroadcastBlock<T>、などを含む多くの異なるブロックがありActionBlock<T>、すべて異なることを行います。asyncそしてもちろん、ライブラリ全体が newおよびawaitキーワードで使用できるように最適化されます。これは、ゆっくりと普及すると思われるエキサイティングな新しいクラスのセットです。

グレースフル ターミネーション

スレッドを停止するにはどうすればよいですか? この質問をよく見かけます。最も簡単な方法は を呼び出すことですThread.Abortが、これを行うことの危険性は誰もが知っています...願っています。これを安全に行うには、さまざまな方法があります。CancellationToken.NET 4.0 では、 と による取り消しと呼ばれる、より統一された概念が導入されましたCancellationTokenSource。バックグラウンド タスクは、安全なポイントでポーリングIsCancellationRequestedまたは単に呼び出して、実行中の作業ThrowIfCancellationRequestedを適切に中断することができます。他のスレッドはCancel、キャンセルを要求するために呼び出すことができます。

于 2012-05-10T18:35:28.307 に答える
7

さてここで見てみましょう:

  1. クラス-ちょっと古いですが、単純な生産者/消費者パターンとしてはThreadPoolまだ信頼できます。
  2. BackgoundWorker(.NET 2.0+)-GUIアプリケーションのバックグラウンドでタスクを実行するための便利な機能を提供する、もう1つの昔ながらの構造。
  3. Timer■-バックグラウンドスレッドを使用して、指定された間隔でコードを実行する場合に便利です。
  4. クラス(Task.NET 4.0+)-基になるスレッドプールで実行され、例外マーシャリングやスケジューリングなどの多くの便利な機能を提供するスレッド抽象化。いわゆる「タスク並列処理」パターンに役立ちます。
  5. Parallel.ForParallel.ForEach(。NET 4.0+)-一連のデータに対して同じ操作を並行して実行するのに適しています。いわゆる「データ並列処理」パターンに役立ちます。
  6. Parallel.Invoke(.NET 4.0+)-sをさらに抽象化しTaskます。いくつかのコード(メソッド、ラムダ)を並行して起動するだけです。
  7. 並行コレクション(.NET 4.0以降)-効率的でスレッドセーフな方法でスレッド間でデータを渡したり共有したりするために必要なすべて。
于 2012-05-10T14:37:06.563 に答える
2

間違いなく、新しいTpl DataFlowライブラリ (.net 4.5 に含まれる) を理解することで、並行開発という点で最大のブーストが得られます。

高度な同時実行アプリに真剣に取り組んでいる場合は、DataFlow に慣れるために 1 日か 2 日を費やしてください。それは真剣に良いです。

于 2012-05-10T12:40:00.880 に答える
1

TaskTask<T>、しかしそれらは .NET 4 以来ここにありました。はasync必ずしもスレッドで動作するとは限りません。非常に良い説明については、Øredev からの Jon のビデオを参照してください。

于 2012-05-10T12:35:12.303 に答える