サンプルコード
なにげなく関数を走らせたら予想外の時間がかかり、タイムアウトの仕組を入れたいと思うことは、プログラマあるあるだと思います。
つい、この間、簡単な方法を見つけました。
- 関数を
Task.Runで走らせる Task.Delayで、タイムアウト用のタイマを走らせる- 2つの
Taskのうち、早く終わったTaskをTask.WaitAnyで検出して次の処理に移る
コンソールアプリケーションのサンプルを以下に示します。
// タイムアウトさせたい時間の入力
Console.Write("タイムアウト時間[ms]を入力してください: ");
var timeout = Console.ReadLine() ?? throw new NullReferenceException();
// 時間のかかる処理をタスクとして実行する(ここでは1秒を要する)
var tasksToBePerformed = Task.Run(() =>
{
const int REQUIRED_TIME = 1000; // 1秒
Task.Delay(REQUIRED_TIME).Wait();
return "関数終了";
});
// 処理とタイムアウトのどちらが先に終了するか待機
var index = Task.WaitAny([tasksToBePerformed, Task.Delay(int.Parse(timeout))]);
// 先に終了した結果を表示
Console.WriteLine(index switch
{
0 => tasksToBePerformed.Result,
1 => "タイムアウト",
_ => throw new InvalidOperationException("Unexpected task index.")
});
初めの3行は、コマンドラインでタイムアウト時間を入力するための行です。
constからreturn迄の3行で関数を表しています。
1秒経ったら”関数終了”という文字列を返します。returnを書かなければvoidをシミュレートします。
勿論、ここに本当の関数を書いていただいても構いません。
関数は、Task.Runで囲んでtasksToBePerformedという名前のタスクとします。
タイムアウト用のタイマは、Task.Delay(int.Parse(timeout))で表現しています。timeoutはコマンドラインで入力するタイムアウト時間です。
これら2つのTaskのうち、早く終了する方のindexをTask.WaitAnyで検出します。
本サンプルは、このindexに基づいて、どちらのTaskが先に終わったかをConsoleに示します。
動作確認

先ず、タイムアウト時間として、500 msを入力してみます。
関数は、1 s(1000 ms)ですので、タイムアウトとなります。

タイムアウト時間として1500 msを入力すると、関数が先に終わるので、「関数終了」と表示されます。
まとめ
なじみの薄い、”Task“を使う必要があります。でも、AwaitやAsyncといった、もっと理解しにくい非同期のおまじないを使う必要はありません。よって、自分には分かり易いです。
リンク



コメント
この処理は待つのをやめるだけで、処理自体は裏で動き続ける設計ですね
abcさん、コメントをありがとうございます。
おっしゃられる通り、処理自体は裏で動き続けています。