今回は、System.IProgress
IProgress
文字通り、進捗状況を処理するための型です。
IProgress
利用方法は、シンプルで
- コンストラクタにActionデリゲートを渡して構築
- ProgressChangedイベントをハンドル
することで、利用できます。
実際に進捗を処理するには、
IProgress<T>.Report(T)
メソッドを呼び出します。
GUIで、別の処理をしながら画面に進捗状況を出力する際に利用できます。
TaskとCancellationTokenとProgress
以前から存在していたBackgroundWorkerと同じような事が実装できます。
Progress
SynchronizationContextをキャプチャして保持します。Reportメソッドが
内部でコールバックを実行する際に、キャプチャしたSynchronizationContext上で
そのメソッドを実行してくれます。なので、そのメソッドの中では、InvokeなしでUI要素にアクセスすることができます。
なので、Progress
UIスレッド上でnewしていないと、キャプチャされるSynchronizationContextが違うものになってしまいます。
このあたりの事情は、BackgroundWorkerの場合と同じですね。
SynchronizationContextについては、以前に記事をアップしていますのでご参照ください。
- .NET クラスライブラリ探訪-018 (AsyncOperation, AsyncOperationManager, SynchronizationContext)(コンテキスト,コンテキストの同期,非同期処理)
- .NET クラスライブラリ探訪-040 (System.Windows.Forms.WindowsFormsSynchronizationContext)(SynchronizationContext, 同期コンテキスト, Send, Post)
以下サンプルです。
なんか、いいサンプルが思いつかなかったので、とりあえずプログレスバーを更新するだけの
ものとなっています。ご勘弁をw
実行すると、プログレスバーが更新されて伸びていき、MAXになったらまた元に戻り伸びていくを
繰り返します。ただし、CancellationTokenSourceをタイムアウト付きで設定しているので
設定したタイムアウト時間を超えたら、自動的にキャンセル扱いとなり、処理が終了します。
上記サンプルの中の
var progress = new Progress<int>(SetProgress); //var progress2 = Task.Run(() => new Progress<int>(SetProgress)).Result; await PerformStep(tokenSource.Token, progress);
となっている部分を、
//var progress = new Progress<int>(SetProgress); var progress2 = Task.Run(() => new Progress<int>(SetProgress)).Result; await PerformStep(tokenSource.Token, progress2);
に変えて、実行すると今度はプログレスバーが一切伸びないようになります。
Progress
がDispatcherSynchronizationContextとならないからです。(この場合、ThreadPoolで実行されます。)
[追記]
なんか、サンプルがあまりにもショボイので、もう一つ作りましたw
こちらもショボイのですが、ちょっとマシかなと。
以下では、カレントフォルダの内容を1ファイルずつ圧縮して、1つのZipファイルを作成しています。
その際の進捗状況の処理にProgress
処理途中でキャンセルされたら、簡易ロールバック処理をしています。
ZipFileクラスの概要については、以下の記事をご参照ください。
- .NET クラスライブラリ探訪-062 (System.IO.Compression.ZipFile (1), .NET 4.5から追加, ZIPファイルの圧縮/解凍)
- .NET クラスライブラリ探訪-063 (System.IO.Compression.ZipFile (2), .NET 4.5から追加, ZIPファイル内のエントリを列挙, ZipArchive, ZipArchiveEntry)
- .NET クラスライブラリ探訪-064 (System.IO.Compression.ZipFile (3), .NET 4.5から追加, ZIPファイルを作成および更新, CreateEntryFromFile, CreateEntry)
そのままだと、処理がすぐに終わってしまうので、わざとTask.Delayを入れています。
以下、参考資料です。正直、機械翻訳の日本語が若干変なので原文読んだ方がわかりやすいと思います。
- IProgress
インターフェイス - Progress
クラス
================================
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場