I have code which calls an external lib synchronous operation, which can take a very long time to finish. I can't rewrite this lib, and there is not a method to stop the operation. Is there any solution to stop this task after some timeout?
I have tried this code, but it does not work not as I expected. It awaits until the calculation is not completed.
How can I solve this task?
private static async Task<ResultData> GetResultAsync(string fileName)
{
var timeoutSource = new CancellationTokenSource(new TimeSpan(0, 5, 0));
try
{
return await Task.Run(() =>
{
var result = ExternLib.Calculate(fileName);
if (result == null)
{
throw new CalculationException(fileName);
}
return result;
},
timeoutSource.Token
).ConfigureAwait(false);
}
catch (AggregateException e)
{
SomeCode(e);
}
catch (OperationCanceledException e)
{
SomeCode2(e);
}
catch (Exception e)
{
SomeCode3(e);
}
return await Task.FromResult<ResultData>(null).ConfigureAwait(false);
}
Create two tasks, one which does the work, and one which acts as a timer:
var workTask = Task.Run(() => // etc );
var timerTask = Task.Delay(TimeSpan.FromMinutes(10));
The wait for either task to complete:
var completedTask = Task.WaitAny(new[] { workTask, timerTask });
Then, if completedTask is the timer task, your timeout has expired, and you can take appropriate action: whether or not you can stop the long running task depends on how it's structured, but you do know you can stop waiting for it.
Related
I have a console app,
{
StartThread();
//must be true, windows system wants to know it is started
return true;
}
I'm trying to create a safety timeout function for this Task. But the task keeps running...
The method DoSomething calls other async methods and awaits their result
Do anyone have an idea why my task don't stop? Maybe a good code example on what to do
public async void StartThread()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
try
{
var timeout = 1000;
Task task = new Task(() => DoSomething(token), token);
task.Start();
if (await Task.WhenAny(task, Task.Delay(timeout, token)) == task)
{
if (token.IsCancellationRequested)
task.Dispose();
await task;
if (task.IsCompleted)
{
task.Dispose();
tokenSource.Cancel();
tokenSource.Dispose();
}
else
{
log.WriteToFile("Timeout_ ");
}
}
else
tokenSource.Cancel();
}
catch (Exception e)
{
Console.WriteLine("--StartThread ...there is an exception----");
}
finally
{
Thread.Sleep(300000); // 5 minutter
StartThread();
}
}
While not create CancellationTokenSource from given timeout?
var timeout = 1000;
//DONE: don't forget to dispose CancellationTokenSource instance
using (var tokenSource = new CancellationTokenSource(timeout)) {
try {
var token = tokenSource.Token;
//TODO: May be you'll want to add .ConfigureAwait(false);
Task task = Task.Run(() => DoSomething(token), token);
await task;
// Completed
}
catch (TaskCanceledException) {
// Cancelled due to timeout
log.WriteToFile("Timeout_ ");
}
catch (Exception e) {
// Failed to complete due to e exception
Console.WriteLine("--StartThread ...there is an exception----");
//DONE: let's be nice and don't swallow the exception
throw;
}
}
You should hardly ever Dispose a Task, since iot is managed by C# internals and it is taken care of. Also, you Dispose way too eagerly, for example:
if (token.IsCancellationRequested)
task.Dispose();
await task;
I do not think so you want still await task if it cancelled and disposed. I guess it will not work at all.
Also if you use async, do not mix blocking calls such as Thread.Sleep - that can lead to disaster...
After all, you use cancellation token with some delay task to imitate a timeout - that's OK, but why do put unnecessary code, when you have great API at hand. Just make use of special contructor of CancellationTokenSource:
public CancellationTokenSource (int millisecondsDelay);
Here's the docs
After a timeout you are setting the CancellationToken and then immediately sleeping the thread for 5 minutes. Thus DoSomething() never gets a chance to continue running and react to the token being cancelled.
I have a method that registers a background task that looks like this:
//snippet from task builder method
try
{
cancellationTokenSource.CancelAfter(10000);
btr = Task.Run(() => registerTask(builder, btr,cancellationTokenSource.Token), cancellationTokenSource.Token).Result;
}
catch (OperationCanceledException) // something went wrong
{
return null;
}
private BackgroundTaskRegistration registerTask(BackgroundTaskBuilder builder, BackgroundTaskRegistration btr, CancellationToken token)
{
CancellationTokenSource newToken = new CancellationTokenSource();
Task cancelledCheck = Task.Run(() =>
{
while (true)
{
if (token.IsCancellationRequested)
{
newToken.Cancel();
token.ThrowIfCancellationRequested();
}
}
}, newToken.Token);
btr = Task.Run(()=> builder.Register(),token).Result;
return btr;
}
My issue is that sometimes the builder.Register() method does not return anything. It's probably a Windows bug of some sort; the Register() method never finishes internally. Indeed, after 10 seconds, the token.ThrowIfCancellationRequested() method is called, but it does not throw to the try-catch statement where it's called. Initially I was calling builder.Register() directly without a Task.Run() but it didn't work, and neither does this.
However, if I replace btr = Task.Run(() =>... with a Task.Delay(ms) instead, where ms > 10000, my intended effect happens.
Am I doing something wrong? Or is there a better way to do this? Basically I just need code that will make the registerTask() method return null when builder.Register() does not finish after a few seconds.
Replacing the code with something like this worked for me:
btr = null;
cancellationTokenSource.CancelAfter(10000);
Task registerTask = Task.Factory.StartNew(() =>
{
btr = builder.Register();
});
Task cancellationTask = Task.Factory.StartNew(() =>
{
while (true)
{
if (cancellationTokenSource.Token.IsCancellationRequested) break;
}
}, cancellationTokenSource.Token);
Task[] tasks = new Task[2] { cancellationTask, registerTask };
Task.WaitAny(tasks);
Instead of handling the error, getting a cancellation request will trigger one of the tasks to end, and when it ends I return the task registration variable whether it's null or not.
Is there a recommended established pattern for self-cancelling and restarting tasks?
E.g., I'm working on the API for background spellchecker. The spellcheck session is wrapped as Task. Every new session should cancel the previous one and wait for its termination (to properly re-use the resources like spellcheck service provider, etc).
I've come up with something like this:
class Spellchecker
{
Task pendingTask = null; // pending session
CancellationTokenSource cts = null; // CTS for pending session
// SpellcheckAsync is called by the client app
public async Task<bool> SpellcheckAsync(CancellationToken token)
{
// SpellcheckAsync can be re-entered
var previousCts = this.cts;
var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
this.cts = newCts;
if (IsPendingSession())
{
// cancel the previous session and wait for its termination
if (!previousCts.IsCancellationRequested)
previousCts.Cancel();
// this is not expected to throw
// as the task is wrapped with ContinueWith
await this.pendingTask;
}
newCts.Token.ThrowIfCancellationRequested();
var newTask = SpellcheckAsyncHelper(newCts.Token);
this.pendingTask = newTask.ContinueWith((t) => {
this.pendingTask = null;
// we don't need to know the result here, just log the status
Debug.Print(((object)t.Exception ?? (object)t.Status).ToString());
}, TaskContinuationOptions.ExecuteSynchronously);
return await newTask;
}
// the actual task logic
async Task<bool> SpellcheckAsyncHelper(CancellationToken token)
{
// do not start a new session if the the previous one still pending
if (IsPendingSession())
throw new ApplicationException("Cancel the previous session first.");
// do the work (pretty much IO-bound)
try
{
bool doMore = true;
while (doMore)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500); // placeholder to call the provider
}
return doMore;
}
finally
{
// clean-up the resources
}
}
public bool IsPendingSession()
{
return this.pendingTask != null &&
!this.pendingTask.IsCompleted &&
!this.pendingTask.IsCanceled &&
!this.pendingTask.IsFaulted;
}
}
The client app (the UI) should just be able to call SpellcheckAsync as many times as desired, without worrying about cancelling a pending session. The main doMore loop runs on the UI thread (as it involves the UI, while all spellcheck service provider calls are IO-bound).
I feel a bit uncomfortable about the fact that I had to split the API into two peices, SpellcheckAsync and SpellcheckAsyncHelper, but I can't think of a better way of doing this, and it's yet to be tested.
I think the general concept is pretty good, though I recommend you not use ContinueWith.
I'd just write it using regular await, and a lot of the "am I already running" logic is not necessary:
Task pendingTask = null; // pending session
CancellationTokenSource cts = null; // CTS for pending session
// SpellcheckAsync is called by the client app on the UI thread
public async Task<bool> SpellcheckAsync(CancellationToken token)
{
// SpellcheckAsync can be re-entered
var previousCts = this.cts;
var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
this.cts = newCts;
if (previousCts != null)
{
// cancel the previous session and wait for its termination
previousCts.Cancel();
try { await this.pendingTask; } catch { }
}
newCts.Token.ThrowIfCancellationRequested();
this.pendingTask = SpellcheckAsyncHelper(newCts.Token);
return await this.pendingTask;
}
// the actual task logic
async Task<bool> SpellcheckAsyncHelper(CancellationToken token)
{
// do the work (pretty much IO-bound)
using (...)
{
bool doMore = true;
while (doMore)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500); // placeholder to call the provider
}
return doMore;
}
}
Here's the most recent version of the cancel-and-restart pattern that I use:
class AsyncWorker
{
Task _pendingTask;
CancellationTokenSource _pendingTaskCts;
// the actual worker task
async Task DoWorkAsync(CancellationToken token)
{
token.ThrowIfCancellationRequested();
Debug.WriteLine("Start.");
await Task.Delay(100, token);
Debug.WriteLine("Done.");
}
// start/restart
public void Start(CancellationToken token)
{
var previousTask = _pendingTask;
var previousTaskCts = _pendingTaskCts;
var thisTaskCts = CancellationTokenSource.CreateLinkedTokenSource(token);
_pendingTask = null;
_pendingTaskCts = thisTaskCts;
// cancel the previous task
if (previousTask != null && !previousTask.IsCompleted)
previousTaskCts.Cancel();
Func<Task> runAsync = async () =>
{
// await the previous task (cancellation requested)
if (previousTask != null)
await previousTask.WaitObservingCancellationAsync();
// if there's a newer task started with Start, this one should be cancelled
thisTaskCts.Token.ThrowIfCancellationRequested();
await DoWorkAsync(thisTaskCts.Token).WaitObservingCancellationAsync();
};
_pendingTask = Task.Factory.StartNew(
runAsync,
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()).Unwrap();
}
// stop
public void Stop()
{
if (_pendingTask == null)
return;
if (_pendingTask.IsCanceled)
return;
if (_pendingTask.IsFaulted)
_pendingTask.Wait(); // instantly throw an exception
if (!_pendingTask.IsCompleted)
{
// still running, request cancellation
if (!_pendingTaskCts.IsCancellationRequested)
_pendingTaskCts.Cancel();
// wait for completion
if (System.Threading.Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA)
{
// MTA, blocking wait
_pendingTask.WaitObservingCancellation();
}
else
{
// TODO: STA, async to sync wait bridge with DoEvents,
// similarly to Thread.Join
}
}
}
}
// useful extensions
public static class Extras
{
// check if exception is OperationCanceledException
public static bool IsOperationCanceledException(this Exception ex)
{
if (ex is OperationCanceledException)
return true;
var aggEx = ex as AggregateException;
return aggEx != null && aggEx.InnerException is OperationCanceledException;
}
// wait asynchrnously for the task to complete and observe exceptions
public static async Task WaitObservingCancellationAsync(this Task task)
{
try
{
await task;
}
catch (Exception ex)
{
// rethrow if anything but OperationCanceledException
if (!ex.IsOperationCanceledException())
throw;
}
}
// wait for the task to complete and observe exceptions
public static void WaitObservingCancellation(this Task task)
{
try
{
task.Wait();
}
catch (Exception ex)
{
// rethrow if anything but OperationCanceledException
if (!ex.IsOperationCanceledException())
throw;
}
}
}
Test use (producing only a single "Start/Done" output for DoWorkAsync):
private void MainForm_Load(object sender, EventArgs e)
{
var worker = new AsyncWorker();
for (var i = 0; i < 10; i++)
worker.Start(CancellationToken.None);
}
Hope this will be useful - tried to create Helper class which can be re-used:
class SelfCancelRestartTask
{
private Task _task = null;
public CancellationTokenSource TokenSource { get; set; } = null;
public SelfCancelRestartTask()
{
}
public async Task Run(Action operation)
{
if (this._task != null &&
!this._task.IsCanceled &&
!this._task.IsCompleted &&
!this._task.IsFaulted)
{
TokenSource?.Cancel();
await this._task;
TokenSource = new CancellationTokenSource();
}
else
{
TokenSource = new CancellationTokenSource();
}
this._task = Task.Run(operation, TokenSource.Token);
}
The examples above seem to have problems when the asynchronous method is called multiple times quickly after each other, for example four times. Then all subsequent calls of this method cancel the first task and in the end three new tasks are generated which run at the same time. So I came up with this:
private List<Tuple<Task, CancellationTokenSource>> _parameterExtractionTasks = new List<Tuple<Task, CancellationTokenSource>>();
/// <remarks>This method is asynchronous, i.e. it runs partly in the background. As this method might be called multiple times
/// quickly after each other, a mechanism has been implemented that <b>all</b> tasks from previous method calls are first canceled before the task is started anew.</remarks>
public async void ParameterExtraction() {
CancellationTokenSource newCancellationTokenSource = new CancellationTokenSource();
// Define the task which shall run in the background.
Task newTask = new Task(() => {
// do some work here
}
}
}, newCancellationTokenSource.Token);
_parameterExtractionTasks.Add(new Tuple<Task, CancellationTokenSource>(newTask, newCancellationTokenSource));
/* Convert the list to arrays as an exception is thrown if the number of entries in a list changes while
* we are in a for loop. This can happen if this method is called again while we are waiting for a task. */
Task[] taskArray = _parameterExtractionTasks.ConvertAll(item => item.Item1).ToArray();
CancellationTokenSource[] tokenSourceArray = _parameterExtractionTasks.ConvertAll(item => item.Item2).ToArray();
for (int i = 0; i < taskArray.Length - 1; i++) { // -1: the last task, i.e. the most recent task, shall be run and not canceled.
// Cancel all running tasks which were started by previous calls of this method
if (taskArray[i].Status == TaskStatus.Running) {
tokenSourceArray[i].Cancel();
await taskArray[i]; // wait till the canceling completed
}
}
// Get the most recent task
Task currentThreadToRun = taskArray[taskArray.Length - 1];
// Start this task if, but only if it has not been started before (i.e. if it is still in Created state).
if (currentThreadToRun.Status == TaskStatus.Created) {
currentThreadToRun.Start();
await currentThreadToRun; // wait till this task is completed.
}
// Now the task has been completed once. Thus we can recent the list of tasks to cancel or maybe run.
_parameterExtractionTasks = new List<Tuple<Task, CancellationTokenSource>>();
}
I have a doubt with cancellation token source which I am using as shown in the below code:
void Process()
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
var cancellationToken = _cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
MainTaskRoutine();
}, cancellationToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
void MainTaskRoutine()
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
//this method shows that a nested task is created
var cancellationToken = _cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
}, cancellationToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
Edit: further elaboration
End objective is: when a user cancels the operation, all the immediate pending tasks(either children or grand children) should cancel.
Scenario:
As per above code:
1. I first check whether user has asked for cancellation
2. If user has not asked for cancellation then only continue with the task (Please see Process method).
sample code shows only one task here but actually there can be three or more
Lets say that CPU started processing Task1 while other tasks are still in the Task queue waiting for some CPU to come and execute them.
User requests cancellation: Task 2,3 in Process method are immediately cancelled, but Task 1 will continue to work since it is already undergoing processing.
In Task 1 it calls method MainTaskRoutine, which in turn creates more tasks.
In the function of MainTaskRoutine I have written: cancellationToken.ThrowIfCancellationRequested();
So the question is: is it correct way of using CancellationTokenSource as it is dependent on Task.WaitAll()?
[EDITED] As you use an array in your code, I assume there could be multiple tasks, not just one. I also assume that within each task that you're starting from Process you want to do some CPU-bound work first (//do some work here), and then run MainTaskRoutine.
How you handle task cancellation exceptions is determined by your project design workflow. E.g., you could do it inside Process method, or from where you call Process. If your only concern is to remove Task objects from the array where you keep track of the pending tasks, this can be done using Task.ContinueWith. The continuation will be executed regardless of the task's completion status (Cancelled, Faulted or RanToCompletion):
Task Process(CancellationToken cancellationToken)
{
var tArray = new List<Task>();
var tArrayLock = new Object();
var task = Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
return MainTaskRoutine(cancellationToken);
}, cancellationToken);
// add the task to the array,
// use lock as we may remove tasks from this array on a different thread
lock (tArrayLock)
tArray.Add(task);
task.ContinueWith((antecedentTask) =>
{
if (antecedentTask.IsCanceled || antecedentTask.IsFaulted)
{
// handle cancellation or exception inside the task
// ...
}
// remove task from the array,
// could be on a different thread from the Process's thread, use lock
lock (tArrayLock)
tArray.Remove(antecedentTask);
}, TaskContinuationOptions.ExecuteSynchronously);
// add more tasks like the above
// ...
// Return aggregated task
Task[] allTasks = null;
lock (tArrayLock)
allTasks = tArray.ToArray();
return Task.WhenAll(allTasks);
}
Your MainTaskRoutine can be structured in exactly the same way as Process, and have the same method signature (return a Task).
Then you may want to perform a blocking wait on the aggregated task returned by Process, or handle its completion asynchronously, e.g:
// handle the completion asynchronously with a blocking wait
void RunProcessSync()
{
try
{
Process(_cancellationTokenSource.Token).Wait();
MessageBox.Show("Process complete");
}
catch (Exception e)
{
MessageBox.Show("Process cancelled (or faulted): " + e.Message);
}
}
// handle the completion asynchronously using ContinueWith
Task RunProcessAync()
{
return Process(_cancellationTokenSource.Token).ContinueWith((task) =>
{
// check task.Status here
MessageBox.Show("Process complete (or cancelled, or faulted)");
}, TaskScheduler.FromCurrentSynchronizationContext());
}
// handle the completion asynchronously with async/await
async Task RunProcessAync()
{
try
{
await Process(_cancellationTokenSource.Token);
MessageBox.Show("Process complete");
}
catch (Exception e)
{
MessageBox.Show("Process cancelled (or faulted): " + e.Message);
}
}
After doing some research I found this link.
The code now looks like this:
see the usage of CancellationTokenSource.CreateLinkedTokenSource in below code
void Process()
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
var cancellationToken = _cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
MainTaskRoutine(cancellationToken);
}, cancellationToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
void MainTaskRoutine(CancellationToken cancellationToken)
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
//this method shows that a nested task is created
using (var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
var cancelToken = cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancelToken.ThrowIfCancellationRequested();
//do some work here
}, cancelToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
}
Note: I haven't used it, but I will let You know once it is done :)
I'm looking for an efficient way to throw a timeout exception if a synchronous method takes too long to execute. I've seen some samples but nothing that quite does what I want.
What I need to do is
Check that the sync method does exceed its SLA
If it does throw a timeout exception
I do not have to terminate the sync method if it executes for too long. (Multiple failures will trip a circuit breaker and prevent cascading failure)
My solution so far is show below. Note that I do pass a CancellationToken to the sync method in the hope that it will honor a cancellation request on timeout. Also my solution returns a task that can then be awaited on etc as desired by my calling code.
My concern is that this code creates two tasks per method being monitoring. I think the TPL will manage this well, but I would like to confirm.
Does this make sense? Is there a better way to do this?
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;
}
Edit:
Using #Timothy's answer I'm now using this. While not significantly less code it is a lot clearer. Thanks!
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;
}
If you have a Task called task, you can do this:
var delay = Task.Delay(TimeSpan.FromSeconds(3));
var timeoutTask = Task.WhenAny(task, delay);
If timeoutTask.Result ends up being task, then it didn't timeout. Otherwise, it's delay and it did timeout.
I don't know if this is going to behave identically to what you have implemented, but it's the built-in way to do this.
I have re-written this solution for .NET 4.0 where some methods are not available e.g.Delay. This version is monitoring a method which returns object. How to implement Delay in .NET 4.0 comes from here: How to put a task to sleep (or delay) in C# 4.0?
public class OperationWithTimeout
{
public Task<object> Execute(Func<CancellationToken, object> operation, TimeSpan timeout)
{
var cancellationToken = new CancellationTokenSource();
// Two tasks are created.
// One which starts the requested operation and second which starts Timer.
// Timer is set to AutoReset = false so it runs only once after given 'delayTime'.
// When this 'delayTime' has elapsed then TaskCompletionSource.TrySetResult() method is executed.
// This method attempts to transition the 'delayTask' into the RanToCompletion state.
Task<object> operationTask = Task<object>.Factory.StartNew(() => operation(cancellationToken.Token), cancellationToken.Token);
Task delayTask = Delay(timeout.TotalMilliseconds);
// Then WaitAny() waits for any of the provided task objects to complete execution.
Task[] tasks = new Task[]{operationTask, delayTask};
Task.WaitAny(tasks);
try
{
if (!operationTask.IsCompleted)
{
// If operation task didn't finish within given timeout call Cancel() on token and throw 'TimeoutException' exception.
// If Cancel() was called then in the operation itself the property 'IsCancellationRequested' will be equal to 'true'.
cancellationToken.Cancel();
throw new TimeoutException("Timeout waiting for method after " + timeout + ". Method was to slow :-)");
}
}
finally
{
cancellationToken.Dispose();
}
return operationTask;
}
public static Task Delay(double delayTime)
{
var completionSource = new TaskCompletionSource<bool>();
Timer timer = new Timer();
timer.Elapsed += (obj, args) => completionSource.TrySetResult(true);
timer.Interval = delayTime;
timer.AutoReset = false;
timer.Start();
return completionSource.Task;
}
}
How to use it then in Console app.
public static void Main(string[] args)
{
var operationWithTimeout = new OperationWithTimeout();
TimeSpan timeout = TimeSpan.FromMilliseconds(10000);
Func<CancellationToken, object> operation = token =>
{
Thread.Sleep(9000); // 12000
if (token.IsCancellationRequested)
{
Console.Write("Operation was cancelled.");
return null;
}
return 123456;
};
try
{
var t = operationWithTimeout.Execute(operation, timeout);
var result = t.Result;
Console.WriteLine("Operation returned '" + result + "'");
}
catch (TimeoutException tex)
{
Console.WriteLine(tex.Message);
}
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
To elabolate on Timothy Shields clean solution:
if (task == await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(3))))
{
return await task;
}
else
throw new TimeoutException();
This solution, I found, will also handle the case where the Task has a return value - i.e:
async Task<T>
More to be found here: MSDN: Crafting a Task.TimeoutAfter Method
Jasper's answer got me most of the way, but I specifically wanted a void function to call a non-task synchronous method with a timeout. Here's what I ended up with:
public static void RunWithTimeout(Action action, TimeSpan timeout)
{
var task = Task.Run(action);
try
{
var success = task.Wait(timeout);
if (!success)
{
throw new TimeoutException();
}
}
catch (AggregateException ex)
{
throw ex.InnerException;
}
}
Call it like:
RunWithTimeout(() => File.Copy(..), TimeSpan.FromSeconds(3));