経緯
製造現場で、外観検査機のデスクトップアプリを開発することになりました。このアプリでは以下のような3つの処理を行います。
📷 カメラ画像の取り込み
🧠 カメラ画像のAI判定
📊 レーザー変位計での寸法等測定とCSV出力
それぞれが独立した処理であり、1つ1つにそれなりの時間がかかります。最初は順番に実行しようと思いましたが、やはり時間がかかりすぎました。ChatGPTに聞いたところ「非同期処理を使え」と言われたので、初めてC#のasync / awaitを学ぶことになりました。
C#の非同期処理とは?
C#では、時間のかかる処理(I/Oや通信など)をメインスレッドを止めずに実行するためにasync / await構文が用意されています。
基本構文
static async Task Main()
{
// 非同期処理を呼び出す
await DoSomethingAsync();
}
static async Task DoSomethingAsync()
{
Console.WriteLine("重い処理を開始...");
// 非同期的に2秒待つ
await Task.Delay(2000);
Console.WriteLine("重い処理が完了!");
}
ポイント
async:メソッドが非同期であることを示す
await:処理の完了を「待つ」が、スレッドはブロックされない
Task:非同期処理の実行単位(戻り値を持つ場合は Task)
Step 1. 並行カウントで動作を可視化
並行でカウントする処理を作ってみました。
1つは1秒ごとに100回、もう1つは2秒ごとに30回カウントします。
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("処理開始");
// 1秒間隔で100回カウントする非同期処理
Task t1 = CountAsync("1秒カウント", 100, 1000);
// 2秒間隔で30回カウントする非同期処理
Task t2 = CountAsync("2秒カウント", 30, 2000);
// 両方の処理が完了するまで待機
await Task.WhenAll(t1, t2);
Console.WriteLine("すべてのカウント完了!");
}
// 指定回数、指定間隔でカウントする非同期メソッド
static async Task CountAsync(string name, int count, int intervalMs)
{
for (int i = 1; i <= count; i++)
{
Console.WriteLine($"{name}: {i}");
// 非同期で待機、CPUは解放される
await Task.Delay(intervalMs);
}
Console.WriteLine($"{name} 完了!");
}
}
実行結果(抜粋)
処理開始
1秒カウント: 1
2秒カウント: 1
1秒カウント: 2
1秒カウント: 3
2秒カウント: 2
...
1秒カウント 完了!
2秒カウント 完了!
すべてのカウント完了!
await Task.WhenAll(...) により、2つの処理が完全に並行で動いていることが分かります。
Step 2. 3セット繰り返して確認する
次は、このカウント処理を3セット繰り返し、各セットごとに「ちゃんとカウントしたか確認」します。
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("処理開始");
// 同じ処理を3回繰り返す
for (int set = 1; set <= 3; set++)
{
Console.WriteLine($"\n==== 第{set}セット開始 ====");
// 並行でカウント処理を開始
Task<int> t1 = CountAsync("1秒カウント", 100, 1000);
Task<int> t2 = CountAsync("2秒カウント", 30, 2000);
// 両方の処理が完了するまで待つ
int[] results = await Task.WhenAll(t1, t2);
// 結果確認
Console.WriteLine($"第{set}セット完了!確認:");
Console.WriteLine($" - 1秒カウント 結果: {results[0]}回");
Console.WriteLine($" - 2秒カウント 結果: {results[1]}回");
Console.WriteLine("===========================");
}
Console.WriteLine("\nすべてのセット完了!");
}
// 指定回数、指定間隔でカウントする非同期メソッド(結果を返す)
static async Task<int> CountAsync(string name, int count, int intervalMs)
{
for (int i = 1; i <= count; i++)
{
Console.WriteLine($"{name}: {i}");
await Task.Delay(intervalMs); // 非同期で待機
}
Console.WriteLine($"{name} 完了!({count}回)");
return count; // 完了した回数を返す
}
}
出力イメージ
==== 第1セット開始 ====
1秒カウント: 1
2秒カウント: 1
...
1秒カウント 完了!(100回)
2秒カウント 完了!(30回)
第1セット完了!確認:
- 1秒カウント 結果: 100回
- 2秒カウント 結果: 30回
===========================
==== 第2セット開始 ====
...
すべてのセット完了!
まとめ
| 学んだこと | 内容 |
|---|---|
async / await
|
非同期処理を直感的に書ける仕組み |
Task |
非同期処理の実行単位 |
Task.WhenAll() |
複数の非同期処理を並行実行して待機 |
await Task.Delay() |
ブロックしない時間待ち |
| ループとの組み合わせ | 非同期処理を順次制御する方法 |