C#の非同期プログラミングを基本から解説!

- システム
エンジニア - 非同期プログラミングという言葉の意味を教えてください。
- プロジェクト
マネージャー - まずは非同期とは何か、基本から見ていきましょう。
非同期とは?
C#の非同期プログラミングについて解説する前に、まず非同期とは何なのかについて簡単に説明します。非同期の処理とは「同期ではない処理」という意味で、同期処理とはプログラミングした順番に処理が進むことです。普通にプログラムすれば同期処理になります。
それに対して非同期処理とは、「複数のプログラムを並列で処理すること」です。A→B→Cの順で処理を行う場合、同期の場合はAの処理が全て終わった後にBを開始し、Bの処理が全て終わった後にCを開始します。
ところが非同期の場合は、AとBとCを平行で同時に処理します。その非同期処理をC#で実現する方法をこれから説明しましょう。
非同期処理のC#サンプル
それではC#の非同期プログラムのサンプルを実行しましょう。以下が簡単な例です。ソースの先頭には「using System.Threading.Tasks;」と記述してください。
static void Main(string[] args)
{
Task t = Task.Run(() => { AsyncWork(); });
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理A実行”+i);
}
Task.WaitAll(t);
}
static void AsyncWork()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理B実行”+i);
}
}
Taskクラスで非同期処理を実行できる
上のC#サンプルを実行すると、「処理A実行」と「処理B実行」が交互でなくバラバラに表示されることが確認できます。非同期処理は「Task t = Task.Run(() => { AsyncWork(); });」で実現しています。
TaskクラスのRunメソッドでMainメソッドと平行に処理をしたいAsyncWorkを指定することで、非同期で処理されるのです。そして、最後のTask.WaitAllでAsyncWorkの終了を待っています。
このような非同期処理は、UIを持つアプリケーションで利用されます。ブラウザでは大きなファイルをダウンロードしている間もWebを閲覧できますが、もし同期処理の場合はダウンロードが終わるまでブラウザを全く使えなくなります。
非同期処理の必要性はそこにあります。
戻り値を取得するC#サンプル
Taskクラスは戻り値を受け取ることができます。ここまでのC#サンプルを以下のように修正してみましょう。
static void Main(string[] args)
{
Task<string> t = Task.Run(() => { return AsyncWork(); });
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理A実行”+i);
}
Console.WriteLine(t.Result);
}
static string AsyncWork()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理B実行”+i);
}
return “完了”;
}
例外をキャッチするC#サンプル
上のC#サンプルを実行すると、最後に「完了」と表示されます。AsyncWorkメソッドの戻り値の文字列を取得できていることがわかります。Task.WaitAllが無くても戻り値を待つ処理があるので、それでAsyncWorkの終わりを待つことが出来ます。
非同期処理中に発生した例外をキャッチしたい場合はどうすればよいのでしょうか。以下がそのC#サンプルです。
static void Main(string[] args)
{
Task t = Task.Run(() => { AsyncWork(); });
try
{
t.Wait();
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static void AsyncWork()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理B実行”+i);
if (i == 50) throw new Exception(“テスト”);
}
}
Waitメソッドで例外発生を待つことが可能
上のC#サンプルを実行すると、最後は以下のように表示されます。
処理B実行50
One or more errors occurred. (テスト)
例外をキャッチできていることがわかりますね。例外を受けるにはこのようにtry~catchの中でWaitメソッドを使用して、待つ必要があります。
また、こういった非同期プログラミングをしていると、複数のタスクを待ってそれぞれの結果を得たいということがあると思います。次の項のサンプルを実行してみてください。
複数のタスクの終了を待つC#サンプル
static void Main(string[] args)
{
Task<int> t1 = Task.Run(() => { return AsyncWork1(); });
Task<int> t2 = Task.Run(() => { return AsyncWork2(); });
Task.WaitAll(t1, t2);
Console.WriteLine(t1.Result + t2.Result);
}
static int AsyncWork1()
{
int sum = 0;
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理A実行”+i);
sum++;
}
return sum;
}
static int AsyncWork2()
{
int sum = 0;
for (int i = 0; i < 50; i++)
{
Console.WriteLine(“処理B実行”+i);
sum++;
}
return sum;
}
WaitAllメソッドで複数のタスクの処理を待てる
上のC#サンプルを実行すると、最後に150と表示されます。AsyncWork1とAsyncWork2の結果を足して表示しています。Task.WaitAllは複数のタスクがすべて終わるまで待つことができるのです。このように待つことを「同期をとる」とも言います。
なおC#の非同期処理は別の記述の仕方もあります。それはasync/awaitを使うやり方です。次の項にそのサンプルを載せます。なお、ソースの先頭に「using System.Threading;」を記述してください。
async/awaitのC#サンプル
static void Main(string[] args)
{
AsyncWork();
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理A実行”+i);
Thread.Sleep(10);
}
}
static async void AsyncWork()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理B実行”+i);
await Task.Delay(10);
}
}
async/awaitだけを使用した時の問題点
上のサンプルを実行すると、処理A実行の表示と処理B実行の表示が並列で行われます。
Mainメソッド内部ではThread.Sleepで他の非同期処理を待ち、asyncにしたAsyncWorkメソッドではawait Task.Delayで他の非同期処理を待っています。awaitはasyncにしたメソッドだけで使用できます。
ただし処理A実行は99の最後まで表示されますが、処理B実行は途中で終わってしまいます。MainメソッドがAsyncWorkが終わるのを待たずに終了してしまうのです。この問題を解決するのが、以下のC#サンプルです。
asyncメソッドの終了を待つC#サンプル
static void Main(string[] args)
{
Task t = AsyncWork();
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理A実行”+i);
Thread.Sleep(10);
}
Task.WaitAll(t);
}
static async Task AsyncWork()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(“処理B実行”+i);
await Task.Delay(10);
}
}
Task.WaitAllでasyncメソッドの終了を待てる
上のサンプルを実行すると、処理B実行の表示も99まで実行されることが確認できます。asyncメソッドにTaskクラスを使用することで、WaitAllで終了を待つことができるのです。
しかし結局Taskクラスによる制御が必要になるのであれば、敢えてasync/awaitを使う意味は無いように思えます。それでもasyncを使ったメソッドは非同期であることがより明確になる、という意義はあるでしょう。
- システム
エンジニア - 複数のプログラムの並列処理が非同期なんですね。
- プロジェクト
マネージャー - 手軽に行うことができますのでまず自分で書いてみて覚えることが重要ですね。
C#の非同期プログラミングは難しくない
非同期処理のプログラミングは難しいイメージがありますが、今回説明したようにC#では手軽に行うことができます。もしC#で時間のかかる処理を行う場合、非同期プログラミングで処理の進捗を表示するなどして活用しましょう。
FEnet.NETナビ・.NETコラムは株式会社オープンアップシステムが運営しています。
株式会社オープンアップシステムはこんな会社です
秋葉原オフィスには株式会社オープンアップシステムをはじめグループのIT企業が集結!
数多くのエンジニアが集まります。

-
スマホアプリから業務系システムまで
スマホアプリから業務系システムまで開発案件多数。システムエンジニア・プログラマーとしての多彩なキャリアパスがあります。
-
充実した研修制度
毎年、IT技術のトレンドや社員の要望に合わせて、カリキュラムを刷新し展開しています。社内講師の丁寧なサポートを受けながら、自分のペースで学ぶことができます。
-
資格取得を応援
スキルアップしたい社員を応援するために資格取得一時金制度を設けています。受験料(実費)と合わせて資格レベルに合わせた最大10万円の一時金も支給しています。
-
東証プライム上場企業グループ
オープンアップシステムは東証プライム上場「株式会社オープンアップグループ」のグループ企業です。
安定した経営基盤とグループ間のスムーズな連携でコロナ禍でも安定した雇用を実現させています。
株式会社オープンアップシステムに興味を持った方へ
株式会社オープンアップシステムでは、開発系エンジニア・プログラマを募集しています。
年収をアップしたい!スキルアップしたい!大手の上流案件にチャレンジしたい!
まずは話だけでも聞いてみたい場合もOK。お気軽にご登録ください。


C#新着案件New Job
-
システム開発/東京都新宿区/【WEB面談可/C#経験者/20代前半の方活躍中/経験1年以上の方活躍中】/在宅勤務
月給29万~34万円東京都新宿区(新宿駅) -
システム開発/東京都新宿区/【WEB面談可/C#経験者/20代後半~40代の方活躍中/経験年数不問】/在宅勤務
月給41万~50万円東京都新宿区(新宿駅) -
デバック、テスト項目の作成/神奈川県横浜市/【WEB面談可/C#経験者/20代前半の方活躍中/経験1年以上の方活躍中】/在宅勤務
月給29万~34万円神奈川県横浜市(桜木町駅) -
デバック、テスト項目の作成/神奈川県横浜市/【WEB面談可/C#経験者/20代後半~40代の方活躍中/経験年数不問】/在宅勤務
月給41万~50万円神奈川県横浜市(桜木町駅) -
基幹システム開発導入/東京都新宿区/【WEB面談可/C#経験者/20代前半の方活躍中/経験1年以上の方活躍中】/在宅勤務
月給29万~34万円東京都新宿区(西新宿駅) -
基幹システム開発導入/東京都新宿区/【WEB面談可/C#経験者/20代後半~40代の方活躍中/経験年数不問】/在宅勤務
月給41万~50万円東京都新宿区(西新宿駅)