同期メソッドの実行に時間がかかりすぎる場合にタイムアウト例外をスローする効率的な方法を探しています。いくつかのサンプルを見てきましたが、私が望むものは何もありません。
私がしなければならないことは
- 同期方法が SLA を超えていることを確認する
- タイムアウト例外をスローする場合
実行時間が長すぎる場合、同期メソッドを終了する必要はありません。(複数の障害が発生すると、回路ブレーカーが作動し、カスケード障害が防止されます)
これまでの私の解決策は以下のとおりです。タイムアウト時にキャンセル要求を受け入れることを期待して、CancellationToken を sync メソッドに渡していることに注意してください。また、私のソリューションは、呼び出し元のコードで必要に応じて待機できるタスクを返します。
私の懸念は、このコードが監視中のメソッドごとに 2 つのタスクを作成することです。TPLはこれをうまく処理すると思いますが、確認したいと思います。
これは理にかなっていますか?これを行うより良い方法はありますか?
private Task TimeoutSyncMethod( Action<CancellationToken> syncAction, TimeSpan timeout )
{
var cts = new CancellationTokenSource();
var outer = Task.Run( () =>
{
try
{
//Start the synchronous method - passing it a cancellation token
var inner = Task.Run( () => syncAction( cts.Token ), cts.Token );
if( !inner.Wait( timeout ) )
{
//Try give the sync method a chance to abort grecefully
cts.Cancel();
//There was a timeout regardless of what the sync method does - so throw
throw new TimeoutException( "Timeout waiting for method after " + timeout );
}
}
finally
{
cts.Dispose();
}
}, cts.Token );
return outer;
}
編集:
@Timothyの答えを使用して、私は今これを使用しています。コードが大幅に減ったわけではありませんが、より明確になりました。ありがとう!
private Task TimeoutSyncMethod( Action<CancellationToken> syncAction, TimeSpan timeout )
{
var cts = new CancellationTokenSource();
var inner = Task.Run( () => syncAction( cts.Token ), cts.Token );
var delay = Task.Delay( timeout, cts.Token );
var timeoutTask = Task.WhenAny( inner, delay ).ContinueWith( t =>
{
try
{
if( !inner.IsCompleted )
{
cts.Cancel();
throw new TimeoutException( "Timeout waiting for method after " + timeout );
}
}
finally
{
cts.Dispose();
}
}, cts.Token );
return timeoutTask;
}