C#は非同期プログラミングをサポートする処理が数多く実装されています。その中で最もシンプルな非同期処理を行わせる方法が Task.Run と呼ばれるものです。Task.Run を使用することで別スレッドに処理を行わせることが可能です。「非同期プログラミングの概要・目的・注意点を解説」でも解説していますが、非同期プログラミングは以上に高度な処理です。そのなかでも Task.Run は最初に押さえておくべき基礎的な手法なので、しっかりと理解しておきましょう。
単純なスレッド処理とは
画面側との同期などを考えない最もシンプルな非同期処理をここでは「単純なスレッド処理」と呼んでいます。単純な処理をワーカースレッドにお願いするだけの記述方法であり、作業がどうなったのかを知ることができないのが特徴です。
C# において戻り値も引数もないスレッド処理では Task というキーワードを使用するのが一般的です。
Task とはその名の通り「仕事」のことです。呼び出され元に結果を処理を返すことなく、処理を依頼したら依頼したままの状態になってしまうことです。A さんに仕事をお願いしたまま、勝手に仕事が行われ完了の報告も何もない状態が近いといえるでしょう。
単純スレッド処理のサンプル
それでは Task キーワードを使った単純なスレッド処理のサンプルを紹介していきます。Visual Studio でコンソールアプリケーションを作成して実際に書いてみてもよいと思います。今回は .Net 6.0 を利用したコンソールアプリケーションでサンプルを作成しています。
最も単純な非同期の使い方として Task.Run があります。Task とはまさに「仕事」のことで、 Run は「走る」「起動する」といった意味がありますね。Task.Run を使うことで処理を別スレッドで行う命令を、CPUに対してすることができます。
//App01
Console.WriteLine("Taskでワーカースレッドに投げる前");
Task.Run(() =>
{
//ワーカースレッド内の処理
Console.WriteLine("重い処理を開始しました。");
Thread.Sleep(10000);
Console.WriteLine("重い処理を終了しました。");
});
Console.WriteLine("Taskでワーカースレッドに投げた後");
Console.ReadLine();
実行結果のコンソール画面はこんな感じになります。
Taskでワーカースレッドに投げる前
Taskでワーカースレッドに投げた後
重い処理を開始しました。
重い処理を終了しました。
ソースコードを確認すると「Task でワーカースレッドに投げる前」と「Task でワーカースレッドに投げた後」の間に別の処理が記載されているのに、メッセージの順番がコーディング通りでなくて不思議に思った人もいるかもしれませんが、これが単純に実装できる非同期のサンプルになります。
処理のみを別スレッドで実行させるには Task.Run(); を使うのが便利です。Run メソッドの引数は Action が来るため、処理の実部分をラムダ式で記述することができます。
public static Task Run(Action action)
今回はあくまでも簡単な処理の実装であるため、Thred.Sleep() を使って 10 秒だけ処理を遅らせるようにしました。画面上に表示されるメッセージから、何か別スレッドで処理させる場合は、メインとなるスレッドから切り離されて動くことが分かります。
この時に Task.Run() で別スレッドに投げられた処理は、依頼元とは別の時間軸で動いているのです。「Task でワーカースレッドに投げる前」と「Task でワーカースレッドに投げた後」が先に表示され、あとから「重い処理を開始しました。」と「重い処理を終了しました。」が表示されているのが証拠です。
別スレッドで処理しているものは、元のスレッドから分離されているため、別スレッドでの処理状況を把握していません。そのため元のスレッドとデータ的な依存関係があると処理の順番がおかしくなり、データの不整合などが発生する可能性があるので注意が必要です。