I've recently reimplemented a whole bunch of async WCF service methods using the cancellation pattern I've seen described in a number of places - where you await a Task.WhenAny
on a started task and a Task.Delay. Of course, the existing tasks aren't cancellable, but that will hopefully be addressed in a later release.
In my case, the default duration of the Task.Delay
is governed by a service setting. In an overwhelming majority of cases, the outcome is that the hoped-for task completes in the requisite amount of time. The setting is often made generous.
Most of (but not all) the examples I've seen do not bother to cancel the Task.Delay
. Is it so cheap it's not worth worrying about? I know that cancellation raises an exception. If I cancel the delay, should I be handling the exception?
Here's the method I've made that all the service methods are calling through:
private async Task<T> GetOrTimeout<T>( Task<T> task, [CallerMemberName] string caller = "" )
{
using ( var cts = new CancellationTokenSource( ) )
{
try
{
var timeout = GetDelay( cts.Token );
var first = await Task.WhenAny( task, timeout );
if ( first == timeout ) throw new TimeoutException( Properties.Resources.TimeoutOccurredInService.Fmt( caller ) );
cts.Cancel( ); //--> haven't been doing this. Should I?
return await task;
}
catch ( Exception ex )
{
throw LoggedFaultException( ex, caller );
}
}
}
...and the method that creates the delay would look like this:
private Task GetDelay( CancellationToken token )
{
return Task
.Delay( Properties.Settings.Default.ServiceMethodTimeout, token )
.ContinueWith( _ => { }, TaskContinuationOptions.ExecuteSynchronously );
}
If I don't cancel the delay, am I holding onto resources way longer than necessary? In particular, I'm worried about the instances that WCF spins up to invoke the service methods. I'm worried they'll cut into the concurrency parameters that the service was configured with. The timeout setting is pretty coarse. It seems wasteful to not cancel, but this is all pretty new stuff to me.
Since cancellation involves exceptions, and since I've been trained to not use exceptions to communicate state, this feels like I've painted myself into some awful anti-pattern that I don't fully understand. Perhaps Task.Delay
isn't the right choice for me. It feels like I've made it more complicated than I should. Any light shed on the situation would be most appreciated.
First of all, this whole issue is probably negligible performance-wise and should only be considered otherwise after testing in a real environment.
However if we dive in, Task.Delay
creates a task that is completed after a certain interval. It does so by creating a new System.Threading.Timer
(which implements IDisposable
) that completes the promise task after the interval using a ThreadPool
thread.
If you use Task.Delay
"a lot" you can have a considerable amount of wasted resources hanging around long after they're useful. If you also add any continuations to the Task.Delay
task with a delegate that captures any references they too will hang around with no reason.
So yes, it's safer to cancel the task instead of letting it run out, though probably not by much.
Task.Delay is worth cancelling when you care about the shutdown speed of your app.
One example is asp.net web applications.
When the server recycles your web-app (when it's being live-updated for example) it needs everything to end fast. If you have tasks waiting in the background, especially registered via QueueBackgroundObject
or a similar technique, it might take a while for the app to shut down.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With