製造業 スマートDX推進課 スマート工場DIY日記

製造業で働いているごく普通の社内SEです。日々の活動で生まれた成果物を共有します。

【C#】await を強制的にタイムアウトさせた

前回使用した HidLibrary で信号の取りこぼしが発生し、ReadReportAsync メソッドで永遠に待機しまう事象が発生した為、無理やりタイムアウトさせた話です。
タイムアウトの引数がありますが、上手く動作しません。

Task の CanselAfter も動作しない為(私の使い方が悪かったかもしれません)
苦肉の策で強制的にタイムアウトするようにしました。

どうしてもタスクをタイムアウトさせたい時の参考になれば幸いです。

ソースコード

        private async Task Timeout(int t, CancellationTokenSource cts)
        {
            for (int i = 0; i < t; i += 10)
            {
                await Task.Delay(10);
                if (cts.IsCancellationRequested) return;
            }
        }

        private async Hoge()
        {
            var cts = new CancellationTokenSource();
            Task<> t = HogeTask();
            await Task.WhenAny(t, Timeout(1000, cts.Token));
            cts.Cancel();
            cts.Dispose();

            var result;
            if (t.IsCompleted)
            {
                result = t.Result;
                t.Dispose();
            }
            else
            {
                //タイムアウト時の処理
                return;
            }
        }

解説

Timeout メソッドは、引数の時間(ms) 待機するだけのメソッドです。

特記事項としては

if (ct.IsCancellationRequested) return;

で、外部からのタスクキャンセルを受け取り、メソッドを終了しています。

 

var cts = new CancellationTokenSource();
Timeout(1000, cts.Token));
cts.Cancel();

キャンセルトークンを Timeout メソッドへ渡し、タスク処理が終了した際にキャンセルをしています。

 

上記がタイムアウトの前準備で、あとは Task.WhenAny でいずれかのタスクが完了すれば処理を進行するというプログラムを書けば完成です。

Task<> t = HogeTask();
await Task.WhenAny(t, Timeout(1000, cts.Token));
var result; if (t.IsCompleted) { result = t.Result; t.Dispose(); } else { //タイムアウト時の処理 return; }

タイムアウト時にタスクをDisposeしてませんが(タスクが完了してないのでDispose出来ない)
ガベージコレクションで自動的に破棄されるので、問題はないと思います。

あとがき

無理やり感があるコードですが、これであればどんなタスクでもタイムアウトすることが可能だと思います。

かなりシンプルに実装出来てよかったです。