I have this asynchronous method :
private static async Task Initializ( ) { /*Do Stuff Here*/ }
I want to be able to monitor the task that results from calling this function :
Task T = Class.Initialize( );
if (T.IsCancelled){ /*Do Stuff Here*/ }
I have in place a CancellationTokenSource.
How can I make T (or the function Initialize) utilize that sources token such that if it is cancelled, T.IsCancelled will be true?
EDIT
I do not know for certain but I think the answer to my question lies within using a TaskCompletionSource object. The answer given by Mike has lead me to this conclusion...
From the documentation
A Task will complete in the TaskStatus.Canceled state under any of
the following conditions:
Its CancellationToken was marked for cancellation before the task
started executing,
The task acknowledged the cancellation request on its already signaled
CancellationToken by throwing an OperationCanceledException that bears
the same CancellationToken.
The task acknowledged the cancellation request on its already signaled
CancellationToken by calling the ThrowIfCancellationRequested method
on the CancellationToken.
Updated:
Use this method:
async Task<Task> UntilCompletionOrCancellation(Task asyncOp, CancellationToken ct)
{
var tcs = new TaskCompletionSource<bool>();
using(ct.Register(() => tcs.TrySetResult(true)))
await Task.WhenAny(asyncOp, tcs.Task);
return asyncOp;
}
Consuming task:
var cts = new CancellationTokenSource();
await UntilCompletionOrCancellation(Class.Initialize, cts.Token);
if (!Class.Initialize.IsCompleted)
{
/*Do Stuff Here*/
}
Another approach is to remove async from Initialize
private static Task Initialize()
{
var tcs = new TaskCompletionSource();
//use TrySetResult or TrySetCancelled
return tcs.Task;
}
You can await this task and check whether is canceled or completed.
It is enough to just throw an OperationCanceledException from your asynchronous method.
The following writes true to the console:
public static void Main()
{
Console.WriteLine(DoSomethingAsync().IsCanceled);
}
private static async Task DoSomethingAsync()
{
throw new OperationCanceledException();
}
A nicer way to support cancellation is to have your asynchronous method take a CancellationToken as a parameter, it may then use this token to check for cancellation, e.g:
public static async Task DoSomethingAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
}
You need to call CancellationToken.ThrowIfCancellationRequested() inside the method and let the exception bubble up.
CancellationTokenSource cts = new CancellationTokenSource();
Task T = Class.Initialize(cts.Token);
if (T.IsCancelled){ /*Do Stuff Here*/ }
private static async Task Initializ(CancellationToken token )
{
/*Do Stuff Here*/
token.ThrowIfCancellationRequested();
/*Do More Stuff Here*/
}
Related
I have a method that contains an infinite loop. I call that method from main method and I want to cancel it if it takes more that 2 seconds.
I tried these approaches
Task.WhenAny with cancellation of the non completed tasks and timeout
How can I cancel Task.WhenAll?
How to cancel a task using a CancellationToken and await Task.WhenAny
and lots of other links, but non has worked.
here is my code
static void Main(string[] args)
{
var cts = new CancellationTokenSource(2000);
var task = Task.Run(() => RunTask(), cts.Token);
await task;
}
public static void RunTask()
{
while (true)
{
Console.WriteLine("in while");
}
}
I also tried to cancel my token but it didn't work.
It is not possible to Dispose the task.
I don't have access to RunTask source code. I just can run it.
I cannot send CancellationToken to my method. How can I terminate RunTask() method?
Cancellation tokens are a way to request cancellation. It's up to the running code to accept that request and actually stop the work. It's not like killing a process. For example, if you had something like:
public static void RunTask(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
Console.WriteLine("in while");
}
}
Then, when it's canceled, the loop will end. However, given that the current code doesn't offer any method of canceling and you cannot modify it, then there is no way to cancel it.
What about this approach:
static void Main(string[] args)
{
var cts = new CancellationTokenSource(2000);
var task = Task.Run(() => RunTask(cts.Token), cts.Token);
await task;
}
public static void RunTask(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
Console.WriteLine("in while");
}
}
I created a small wrapper around CancellationToken and CancellationTokenSource. The problem I have is that the CancelAsync method of CancellationHelper doesn't work as expected.
I'm experiencing the problem with the ItShouldThrowAExceptionButStallsInstead method. To cancel the running task, it calls await coordinator.CancelAsync();, but the task is not cancelled actually and doesn't throw an exception on task.Wait
ItWorksWellAndThrowsException seems to be working well and it uses coordinator.Cancel, which is not an async method at all.
The question why is the task is not cancelled when I call CancellationTokenSource's Cancel method in async method?
Don't let the waitHandle confuse you, it's only for not letting the task finish early.
Let the code speak for itself:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace TestCancellation
{
class Program
{
static void Main(string[] args)
{
ItWorksWellAndThrowsException();
//ItShouldThrowAExceptionButStallsInstead();
}
private static void ItShouldThrowAExceptionButStallsInstead()
{
Task.Run(async () =>
{
var coordinator = new CancellationHelper();
var waitHandle = new ManualResetEvent(false);
var task = Task.Run(() =>
{
waitHandle.WaitOne();
//this works well though - it throws
//coordinator.ThrowIfCancellationRequested();
}, coordinator.Token);
await coordinator.CancelAsync();
//waitHandle.Set(); -- with or without this it will throw
task.Wait();
}).Wait();
}
private static void ItWorksWellAndThrowsException()
{
Task.Run(() =>
{
var coordinator = new CancellationHelper();
var waitHandle = new ManualResetEvent(false);
var task = Task.Run(() => { waitHandle.WaitOne(); }, coordinator.Token);
coordinator.Cancel();
task.Wait();
}).Wait();
}
}
public class CancellationHelper
{
private CancellationTokenSource cancellationTokenSource;
private readonly List<Task> tasksToAwait;
public CancellationHelper()
{
cancellationTokenSource = new CancellationTokenSource();
tasksToAwait = new List<Task>();
}
public CancellationToken Token
{
get { return cancellationTokenSource.Token; }
}
public void AwaitOnCancellation(Task task)
{
if (task == null) return;
tasksToAwait.Add(task);
}
public void Reset()
{
tasksToAwait.Clear();
cancellationTokenSource = new CancellationTokenSource();
}
public void ThrowIfCancellationRequested()
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
public void Cancel()
{
cancellationTokenSource.Cancel();
Task.WaitAll(tasksToAwait.ToArray());
}
public async Task CancelAsync()
{
cancellationTokenSource.Cancel();
try
{
await Task.WhenAll(tasksToAwait.ToArray());
}
catch (AggregateException ex)
{
ex.Handle(p => p is OperationCanceledException);
}
}
}
}
Cancellation in .NET is cooperative.
That means that the one holding the CancellationTokenSource signals cancellation and the one holding the CancellationToken needs to check whether cancellation was signaled (either by polling the CancellationToken or by registering a delegate to run when it is signaled).
In your Task.Run you use the CancellationToken as a parameter, but you don't check it inside the task itself so the task will only be cancelled if the token was signaled before the task had to a chance to start.
To cancel the task while it's running you need to check the CancellationToken:
var task = Task.Run(() =>
{
token.ThrowIfCancellationRequested();
}, token);
In your case you block on a ManualResetEvent so you wouldn't be able to check the CancellationToken. You can register a delegate to the CancellationToken that frees up the reset event:
token.Register(() => waitHandle.Set())
I have controller, where I create a Cancellation Token in the constructor and use it in a long running await task (). Does disposing the Cancellation token in Controller.Dispose() causes the long running task to cancel?
public class SomeController : BaseInternalController
{
private CancellationTokenSource cancellationTokenSource;
public MyController()
{
cancellationTokenSource = new CancellationTokenSource();
}
public async Task<HttpResponseMessage> Post(SomeData data)
{
foreach (var item in data)
{
await longRunningTask(item, cancellationTokenSource.token);
}
}
protected override void Dispose(bool disposing)
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();
cancellationTokenSource = null;
}
base.Dispose(disposing);
}
}
Does disposing the Cancellation token in Controller.Dispose() causes
the long running task to cancel?
Depends on how your longRunningTask was implemented.
In this method you should explicitly check whether the cancellation is requested:
token.ThrowIfCancellationRequested();
After invoking of this method your task will be cancelled.
Cancellation example
If in the following example the ThrowIfCancellationRequested would not be invoked, the task would be run forever:
var cts = new CancellationTokenSource();
Task.Run(() =>
{
while (true)
cts.Token.ThrowIfCancellationRequested();
}, cts.Token);
You can learn more about cancellation here.
Note that after setting the cancellationTokenSource to null, you can get NullReferenceException in your foreach loop. I would suggest to copy your token into a local variable:
public async Task<HttpResponseMessage> Post(SomeData data)
{
var token = cancellationTokenSource.Token;
foreach (var item in data)
{
await longRunningTask(item, token);
}
}
Given the following code:
public async Task Send() // part of Sender class
{
// sync code
}
// //
private async Task HandleMessage()
{
// await sender.Send(); // exits HandleMessage immediately
sender.Send().Wait(); // works as expected, waiting to complete
DoOtherStuff(); // doesn't get hit with await
return;
}
RunRecurringTask(async () => await HandleMessage(), result);
public void RunRecurringTask(Action action, RecurringTaskRunResult result)
{
action();
result.DoStuff();
}
I thought that await tells the thread to come back when the awaited thing is complete, but it looks like for some reason that's not happening: the remaining code is never hit and everything just... stops. What could be causing this?
This is a console application in an Azure WebJob, for what it's worth. When Wait is used, I get the expected results, however with await, the job just completes.
You should never do async void unless you are writing a event handler. A Action with the async modifier is a async void method. You need to make the argument a Func<Task> and then do await action() in your RunRecurringTask
private async Task HandleMessage()
{
await sender.Send();
DoOtherStuff();
return;
}
RunRecurringTask(async () => await HandleMessage(), result);
//You also could do
//RunRecurringTask(() => HandleMessage(), result);
public async Task RunRecurringTask(Func<Task> action, RecurringTaskRunResult result)
{
await action();
result.DoStuff();
}
If you had other methods that where not marked with async you will need to change all of them up the call stack till you get to the entry point from the SDK. The SDK understands how to handle functions with a async Task return type since the 0.4.0-beta version.
i am creating a task scheduler so i am trying to make some kind of repeating function that accepts Task and awaits it but i get a strange Exception of Type 'T' is not awaitable
public static Task<T> Interval<T>(TimeSpan pollInterval, Func<T> action, CancellationToken token)
{
return Task.Factory.StartNew(
async () =>
{
for (; ; )
{
if (token.WaitCancellationRequested(pollInterval))
break;
await action();
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
So can anyone tell me how could i await a that generic Task cuz i want the function to accept any Task, Task, bool or any other type ?
You don't need to start a long running task for this - just make your method asynchronous directly:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
while(true)
{
await Task.Delay(pollInterval, token);
action();
}
}
This will cause the Action to run on the current context. If that is not required, you can use:
await Task.Delay(pollInterval, token).ConfigureAwait(false);
action();
This will cause the Action to not run on the same synchronization context of the caller, and potentially use a ThreadPool thread.
Edit in response to comments:
If you don't want the resulting task to come back canceled, but just return when the token is fired, you could use:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
action();
}
catch(OperationCanceledException e)
{
// Swallow cancellation - dangerous if action() throws this, though....
break;
}
}
}
Edit 2:
If you want to pass in async lambdas, you should make the method take an Func<Task>, not Action:
public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Func<Task> actionTask, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
}
catch(OperationCanceledException e)
{
// Swallow cancellation
break;
}
await actionTask();
}
}
Edit in response to chat:
If you want to poll, but use the results of an operation, you could use:
public static async Task RunAtIntervalAsync<T>(TimeSpan pollInterval, Func<Task<T>> fetchOperation, Action<T> operationOnResult, CancellationToken token)
{
while(!token.IsCancellationRequested)
{
try
{
await Task.Delay(pollInterval, token);
}
catch(OperationCanceledException e)
{
// Swallow cancellation
break;
}
// Get a value
T value = await fetchOperation();
// Use result (ie: update UI)
operationOnResult(value);
}
}
You could then call this via:
RunAtIntervalAsync(TimeSpan.FromSeconds(1),
async () => { await Task.Delay(1000); return "Foo"; },
result => UpdateUI(result),
token);
You can't.
You can make a function that takes a generic asynchronous function – a function that returns a Task<T>.
That would be a Func<Task<T>>.
You can also make a function that takes a generic synchronous function, which is what you have now.
You can't make a single function that can take either, but you can make two overloads.
On an unrelated note, your function never actually uses the return value of the function.
Therefore, you shouldn't make it generic at all; you should instead take a Func<Task> or an Action.
Check an example here post.
Agree with SLaks, you need to make the generic parameter T of Func awaitable in order to use the await.
For example if T is a string the code would "await" for a function that returns just a string.
The await is valid only for Tasks. For more info check this explanation MSDN Blog; the example is in VB.net.