重たい処理をおこなう場合に非同期処理をよく用いますが、
その処理の進捗状況がわからないと、
本当に処理が実行されているのかどうかがわかりません。
ここでは async/await 演算子による非同期処理で進捗状況を取得する例を紹介します。
サンプルプロジェクトはここからダウンロードできます。
ページ内リンク
概要
ここでは「async/await による非同期処理 その 1」で示したサンプルプログラム の UI 部分をそのまま流用します。
基本的な非同期処理の記述
非同期処理の記述は「async/await による非同期処理 その 1」で示したサンプルプログラムとほぼ同じですが、
一部だけ変更したいと思います。
まず、ViewModel が公開するプロパティは以下のようにそのまま流用します。
そして、ButtonCommand プロパティから呼ばれる HeavyWorkAsync() メソッドもそのまま流用します。
ここからが少し違います。上記コードで HeavyWork() メソッドをコールしていた部分が HeavyWorkTask() メソッドをコールしていますね。
HeavyWorkTask() メソッドは次のようなコードになります。
このままでは単に HeavyWork() メソッドを非同期処理するだけで、
その進捗状況を知ることができません。次は進捗状況を取得できるように
Code 3 のメソッドを変更していきます。
進捗状況を報告するようにする
進捗状況を報告するには IProgress<T> インターフェースを使用します。
HeavyWork() メソッドを次のように変更します。
IProgress<T> には Report() メソッドがあり、
このメソッドに進捗を示すパラメータを渡すことで進捗状況を報告します。
つまり、進捗が進んだら必ず Report() メソッドをコールする必要があります。
続いて、HeavyWork() メソッドを呼び出す HeavyWorkTask() メソッドを次のように変更します。
HeavyWork() メソッドに入力引数を追加したので、
その引数を指定するコードが追加されています。
Progress<T> クラスのコンストラクタでは、
Report() メソッドがコールされたときに実行される処理を指定します。
このとき、その入力引数は Report() メソッドをコールしたときに渡した T 型のパラメータとなります。
ここでは Result プロパティを、この入力引数を含めた形で変更しています。
通常、UI 更新に関連するプロパティ等は UI スレッド上からのみアクセスが許可されているため、
例えばデータバインド機能によって UI と連携している Result プロパティは、
UI スレッド以外で実行されている HeavyWork() メソッド内からは操作できず、
仮に操作しようとすると例外が発生してしまいます。
しかし、IProgress<T> の Report() メソッドは、
その指定された処理の実体は UI スレッド上で処理されるため、
上記のように Result プロパティにアクセスすることができます。
進捗率の計算に関してはそれぞれのアプリケーションによって異なります。
今回のサンプルでは、1 〜 100 までの数値を Report() メソッドに渡し、
これを進捗率として [%] で表記しましたが、
Report() メソッドで渡す数値が [%] そのものではないかもしれないし、
そもそも処理の全体を知っているのは HeavyWork() メソッドではなく Progress クラスのインスタンスを持つクラスのほうかもしれません。
この部分はそれぞれのアプリケーションに応じて変更することになります。