How to implement Async task for timer in C #?

I want the given operation to be performed for a certain period of time. After this time, send another run command.

StartDoingStuff(); System.Threading.Thread.Sleep(200); StopDoingStuff(); 

Instead of having a sleep statement that blocks the rest of the application, how can I write this using Async / Task / Await in C #?

+8
multithreading c # async-await
source share
3 answers

This question was answered by Joe Hoag on the Parallel Team blog in 2011: Creating a task. TimeoutAfter Method .

The solution uses the TaskCompletionSource object and includes several optimizations (12%, just avoiding captures), handles cleaning and covers extreme cases, such as calling TimeoutAfter, when the target task is already completed, sending invalid timeouts, etc.

The beauty of Task.TimeoutAfter is that it is very easy to put it together with other extensions, because it does only one thing: it notifies you that the timeout has expired. He is not trying to cancel your task. You decide what to do when a TimeoutException is thrown.

A brief implementation using Stephen async/await 's async/await also provided, although cross cases are also not covered.

Optimized implementation:

 public static Task TimeoutAfter(this Task task, int millisecondsTimeout) { // Short-circuit #1: infinite timeout or task already completed if (task.IsCompleted || (millisecondsTimeout == Timeout.Infinite)) { // Either the task has already completed or timeout will never occur. // No proxy necessary. return task; } // tcs.Task will be returned as a proxy to the caller TaskCompletionSource<VoidTypeStruct> tcs = new TaskCompletionSource<VoidTypeStruct>(); // Short-circuit #2: zero timeout if (millisecondsTimeout == 0) { // We've already timed out. tcs.SetException(new TimeoutException()); return tcs.Task; } // Set up a timer to complete after the specified timeout period Timer timer = new Timer(state => { // Recover your state information var myTcs = (TaskCompletionSource<VoidTypeStruct>)state; // Fault our proxy with a TimeoutException myTcs.TrySetException(new TimeoutException()); }, tcs, millisecondsTimeout, Timeout.Infinite); // Wire up the logic for what happens when source task completes task.ContinueWith((antecedent, state) => { // Recover our state data var tuple = (Tuple<Timer, TaskCompletionSource<VoidTypeStruct>>)state; // Cancel the Timer tuple.Item1.Dispose(); // Marshal results to proxy MarshalTaskResults(antecedent, tuple.Item2); }, Tuple.Create(timer, tcs), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); return tcs.Task; } 

and Stephen Toub, without checks for edge cases:

 public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) { if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) await task; else throw new TimeoutException(); } 
+10
source share

Assuming that StartDoingStuff and StopDoingStuff were created as Async methods returning Task, then

 await StartDoingStuff(); await Task.Delay(200); await StopDoingStuff(); 

EDIT: If the original questionnaire wants the asynchronous method to be canceled after a certain period of time: it is assumed that the method will not make any network requests, but simply performs some processing in memory, and the result can be canceled arbitrarily without taking into account its effects, then use undo marker:

  private async Task Go() { CancellationTokenSource source = new CancellationTokenSource(); source.CancelAfter(200); await Task.Run(() => DoIt(source.Token)); } private void DoIt(CancellationToken token) { while (true) { token.ThrowIfCancellationRequested(); } } 

EDIT : I should have mentioned that you can catch the resulting OperationCanceledException by indicating how the Task ended, avoiding the need to bother with bools.

+2
source share

Here is how I would do it using the task annulment template (the option does not throw an exception).

[EDITED] Updated to use the Svick clause to set a timeout through the CancellationTokenSource constructor.

 // return true if the job has been done, false if cancelled async Task<bool> DoSomethingWithTimeoutAsync(int timeout) { var tokenSource = new CancellationTokenSource(timeout); CancellationToken ct = tokenSource.Token; var doSomethingTask = Task<bool>.Factory.StartNew(() => { Int64 c = 0; // count cycles bool moreToDo = true; while (moreToDo) { if (ct.IsCancellationRequested) return false; // Do some useful work here: counting Debug.WriteLine(c++); if (c > 100000) moreToDo = false; // done counting } return true; }, tokenSource.Token); return await doSomethingTask; } 

Here's how to call it the async method:

 private async void Form1_Load(object sender, EventArgs e) { bool result = await DoSomethingWithTimeoutAsync(3000); MessageBox.Show("DoSomethingWithTimeout done:" + result); // false if cancelled } 

Here's how to call it from a regular method and handle asynchronous completion:

 private void Form1_Load(object sender, EventArgs e) { Task<bool> task = DoSomethingWithTimeoutAsync(3000); task.ContinueWith(_ => { MessageBox.Show("DoSomethingWithTimeout done:" + task.Result); // false is cancelled }, TaskScheduler.FromCurrentSynchronizationContext()); } 
+2
source share

All Articles