このコードスニペットは Stephen Clearyのブログ からのものであり、Task.Runを使用するときに進捗状況を報告する方法の例を示しています。 UIの更新にクロススレッドの問題がない理由を知りたいのですが、それはなぜinvokeが必要ないのかということです。
private async void button2_Click(object sender, EventArgs e)
{
var progressHandler = new Progress<string>(value =>
{
label2.Text = value;
});
var progress = progressHandler as IProgress<string>;
await Task.Run(() =>
{
for (int i = 0; i != 100; ++i)
{
if (progress != null)
progress.Report("Stage " + i);
Thread.Sleep(100);
}
});
label2.Text = "Completed.";
}
Progress<T>
は、インスタンス化されたときに現在のSynchronisationContext
をキャッチします。 Report
を呼び出すたびに、それを密かにキャプチャされたコンテキストに委任します。この例では、キャプチャされたコンテキストはUIであり、例外は発生しません。
Progress<T>
コンストラクターは、現在のSynchronizationContext
オブジェクトをキャプチャします。
SynchronizationContext
クラスは、関連するスレッドモデルの詳細を抽象化する機能です。つまり、WindowsフォームではControl.Invoke
を使用し、WPFではDispatcher.Invoke
などを使用します。
progress.Report
オブジェクトが呼び出されると、Progress
オブジェクト自体は、キャプチャされたSynchronizationContext
を使用してデリゲートを実行する必要があることを認識します。
言い換えると、Progress
は開発者が明示的に言うことなく、それを処理するように設計されているため機能します。
このクロススレッド機構の一部が開発者の目から隠されているという事実のために混乱しているようです。そのため、「使用して使用」するだけです。 http://blogs.msdn.com/b/dotnet /archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx
IProgressインターフェイスを導入して、進行状況を表示するためのエクスペリエンスを作成できるようにしました。このインターフェイスは、非同期タスクが呼び出して進行状況を報告するReport(T)メソッドを公開します。このインターフェイスをasyncメソッドのシグネチャで公開し、呼び出し元はこのインターフェイスを実装するオブジェクトを提供する必要があります。一緒に、タスクと呼び出し元は非常に便利なリンケージを作成します(異なるスレッドで実行される可能性があります)。
IProgressの実装であるProgressクラスも提供しました。同期コンテキストの保存と復元に関するすべての記録を処理するため、実装でProgressを使用することをお勧めします。 Progressは、イベントとActionコールバックの両方を公開します。これらは、タスクが進捗を報告するときに呼び出されます。このパターンを使用すると、進行中の変更が発生したときに単純に反応するコードを作成できます。 IProgressとProgressを併用すると、バックグラウンドタスクからUIスレッドに進捗情報を簡単に渡すことができます。
あともう1つ、進行状況の通知が呼び出されます後にジョブの一部が実行され、だけでなくその瞬間。そのため、UIスレッドがアイドリング状態で、予備のCPUコアがある場合、遅延はほぼゼロになります。 UIスレッドがビジーの場合、UIスレッドがアイドル状態に戻るまで(コンピューターのCPUコアの量に関係なく)、通知は呼び出されません。