【C#】async/await Taskについて
僕は意図的に「async/await Task」を使用するのを避けてきました。
何でかっていうと、難しいから。この一言に尽きます。
しかし、今回はその表層部分のみをしれっと書いていきたいと思います。
僕は今でも非同期処理はBackgroundWorkerで良いじゃん!って思います。
さて、それでは、非同期処理がなぜ必要なのか見ていくことにしましょう。
まずは以下のような感じでWindowsフォームアプリケーションを作成します。
※Cancelはあとで使います。
それに以下のようなコードを書き、Startを押すと×ボタンすら押せなくなり、何もできなくなります。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { HeavyMethod(); } private void HeavyMethod() { while (true) { System.Threading.Thread.Sleep(1000); } } }
しかし、これをTaskを使って実行してやると、Taskが別スレッドで実行され、ボタンが押せるようになります。
そこで、キャンセルも簡単に実装できるやん!って書くとこうなります。
public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Task.Run(()=>HeavyMethod()); if (cancel) { MessageBox.Show("キャンセルしました。"); } else { MessageBox.Show("完了しました。"); } } bool cancel = false; private void button2_Click(object sender, EventArgs e) { cancel = true; } private void HeavyMethod() { while (!cancel) { System.Threading.Thread.Sleep(1000); } }
これを実行してStartを押すと「完了しました。」ダイアログがすぐに表示されます。
ここで登場するのがawait/asyncになります。
以下のように書く事で、HeavyMethodを待つようになり、Cancelボタンを押した後で「キャンセルしました。」とダイアログが表示されるようになります。
Taskで呼んだ別スレッド処理をawaitで待つ。(asyncはお約束)みたいな感じでしょうか。
private async void button1_Click(object sender, EventArgs e) { await Task.Run(()=>HeavyMethod()); if (cancel) { MessageBox.Show("キャンセルしました。"); } else { MessageBox.Show("完了しました。"); } }
また、Taskの例外処理は通常のコードの例外処理と同じ感じで書くことができます。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { try { await Task.Run(() => HeavyMethod()); if (cancel) { MessageBox.Show("キャンセルしました。"); cancel = false; } else { MessageBox.Show("完了しました。"); } } catch (Exception ex) { MessageBox.Show(ex.Message); } } bool cancel = false; private void button2_Click(object sender, EventArgs e) { cancel = true; } private void HeavyMethod() { int i = 0; while (!cancel && i++ < 5) { System.Threading.Thread.Sleep(1000); throw new ApplicationException("エラーが発生しました。"); } } }
また、キャンセルに関しても用意されているものがあるので、それを使用した方が良いでしょう。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { try { ctokens = new CancellationTokenSource(); await Task.Run(() => HeavyMethod(ctokens)); MessageBox.Show("完了しました。"); } catch (OperationCanceledException oce) { MessageBox.Show("キャンセルしました。"); } catch (Exception ex) { MessageBox.Show(ex.Message); } ctokens = null; } CancellationTokenSource ctokens = null; private void button2_Click(object sender, EventArgs e) { if (ctokens != null) ctokens.Cancel(); } private void HeavyMethod(CancellationTokenSource ctoken) { int i = 0; while (i++ < 5) { System.Threading.Thread.Sleep(1000); ctoken.Token.ThrowIfCancellationRequested(); } } }
今度重い処理がある場合、積極的にasync/await Taskを使っていきたいと思います。