C#で、asyncを利用した別タスクから、UIコントロールをさわる

TOC

  1. もっとコンパクトに
  2. 繰り返し行う

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(); // 1.
label1.Text = heavyResult;
}
async Task<string> runTask()
{
await Task.Delay(1000); // 2.
return "hogehoge"; // 3.
}

番号はコード内のコメントの番号と対応しています。

  1. 非同期でrunTask関数を実行
  2. 別Taskでの動作の確認のために、1秒間止める。この間もUIは動作する。
  3. 1秒間たった後、文字列hogehogeを返す。
  4. 戻り値を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に書きだすタスクと文字列を送るタスクを分けたほうがよい。

ということだったので、変更しました。