C#
task

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

More than 3 years have passed since last update.

C#で、重い処理をしているときなどに、マルチスレッドで、

別タスクからUIをさわりたいときがあるのですが、エラーが出てしまいます。

そんなときは、Task型の関数を、awaitで実行すればいいのです。



taskUI.cs

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に書きだす。


  • Mainメソッドに、asyncをつけています

また、タスクを別のメソッドにしなくてもできるようです。


taskUI2.cs

using System.Threading.Tasks;

// 省略

async void Main()
{
var heavyResult = await Task.Run(async () =>
{
await Task.Delay(1000);
return "fugafuga";
}
label1.Text = heavyResult;
}



何回もLabelを書き替えたい

なんか重い処理が、「何%進みました」みたいな処理をやるときは、

複数回Labelを書き替える必要があります。

その時は、Taskをつなげて行うのではなく、1%ごとに分けて行うとできます。

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}% 完了しました";
}


追記:Reactive Extensions

Reactive Extensionsを使うと、複数のタスクに分けず、JavaScriptのコールバックみたいなことができます。

デフォルトでは、Reactive Extensionsが入っていないので、

NuGetで「Rx-main」をインストールしてください。

詳細: https://msdn.microsoft.com/en-us/data/gg577610.aspx#dotnet

using System.Reactive.Linq;

using System.Reactive.Disposables;
using System.Threading.Tasks;

void Main()
{
Observable.Create<string>(async o =>
{
for (var i = 0; i < 100; i++)
{
await Task.Delay(100);
o.OnNext($"{i}% 完了しました。");
}
o.OnNext("最後の処理をしています...");
await Task.Delay(1000);
o.OnNext("完了");
return Disposable.Empty;
}).Subscribe(x =>
{
label1.Text = x;
});
}

この方法だと、毎回違う処理をする場合でも、一つのObservableで処理ができます。

NetSeedさん、chocolamintさん、ありがとうございます。