C#で、asyncを利用した別タスクから、UIコントロールをさわる
C#で、重い処理をしているときなどに、マルチスレッドで、
別タスクからUIをさわりたいときがあるのですが、エラーが出てしまいます。
そんなときは、Task型の関数を、awaitで実行すればいいのです。
例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| using System.Threading.Tasks;
async void Main() { var heavyResult = await runTask(); label1.Text = heavyResult; } async Task<string> runTask() { await Task.Delay(1000); return "hogehoge"; }
|
番号はコード内のコメントの番号と対応しています。
- 非同期でrunTask関数を実行
- 別Taskでの動作の確認のために、1秒間止める。この間もUIは動作する。
- 1秒間たった後、文字列hogehogeを返す。
- 戻り値をlabelに書きだす。
別タスクで処理を行っているため、1秒待っている間も、UIは応答します。
もっとコンパクトに
別の関数に分けなくてもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| using System.Threading.Tasks;
async void Main() { var result = await Task.Run(async () => { await Task.Delay(1000); return "hogehoge"; }); label1.Text = result; }
|
繰り返し行う
繰り返し行うときは、一回一回、別のタスクとして実行します。
次の例は、1秒経って hogehoge
と表示し、さらに1秒後に fugafuga
と表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using System.Threading.Tasks;
async void Main() { label1.Text = await Task.Run(async () => { await Task.Delay(1000); return "hogehoge"; }); label1.Text = await Task.Run(async () => { await Task.Delay(1000); return "fugafuga"; }); }
|
応用して、「何% 完了しました」のような処理もできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| using System.Threading.Tasks;
async void Main() { for (var i = 0; i < 100; i++) { label1.Text = await progress(i); } } async Task<string> progress(int percent) { await Task.Delay(100); return $"{percent}% 完了しました"; }
|
追記
Qiitaで、NetSeedさんに教えていただきました。
- Invokeメソッドとasync/awaitが一緒になっていたので、async/awaitの良さがわかりにくい。
- Labelに書きだすタスクと文字列を送るタスクを分けたほうがよい。
ということだったので、変更しました。