我正在寻找一种有效的方法,在同步方法执行时间过长时抛出超时异常。我已经看过一些示例,但都没有完全符合我的需求。
我需要做到以下几点:
1. 检查同步方法是否超出其SLA 2. 如果超时,则抛出超时异常
我不需要在同步方法执行时间过长时终止它。(多个失败将会触发熔断器并防止级联故障)
迄今为止,我的解决方案如下。请注意,我向同步方法传递了一个CancellationToken,希望它能够在超时时响应取消请求。此外,我的解决方案返回一个任务,然后可以按照调用代码的要求进行等待等操作。
我的担忧是,这段代码每个被监视的方法都会创建两个任务。我认为TPL会很好地处理这个问题,但我想确认一下。
这样做有意义吗?有更好的方法吗?
我需要做到以下几点:
1. 检查同步方法是否超出其SLA 2. 如果超时,则抛出超时异常
我不需要在同步方法执行时间过长时终止它。(多个失败将会触发熔断器并防止级联故障)
迄今为止,我的解决方案如下。请注意,我向同步方法传递了一个CancellationToken,希望它能够在超时时响应取消请求。此外,我的解决方案返回一个任务,然后可以按照调用代码的要求进行等待等操作。
我的担忧是,这段代码每个被监视的方法都会创建两个任务。我认为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;
}
Task.Run
而不是TaskFactory.StartNew
。请参考Stephen Toub的博客,如果这还不够有说服力,可以查看我的博客。 - Stephen Cleary