Confusion with Task.Run, Task.Wait using CancellationToken - c#

I have a CancelationTokenSource field in my class
CancelationTokenSource cancelSource;
I have the following code that works and it correctly will 'cancel' if i invoke the cancelSource.Cancel(): or if it times out
public byte[] Result()
{
cancelSource = new CancelationTokenSource(1000); // 1000ms timeout
Func<Byte[]> f = .. // function that returns a byte[]
Task<Byte[]> t = Task.Run(f);
t.Wait(cancelTokenSource.Token);
return t.Result;
}
Then i noticed i can pass the cancelTokenSource.Token directly into the Task.Run instead of inside the Task.Wait()
public byte[] Result()
{
CancelationTokenSource = new CancelationTokenSource(1000); // 1000ms timeout
Func<Byte[]> f = .. // function that returns a byte[]
Task<Byte[]> t = Task.Run(f, cancelTokenSource.Token);
t.Wait();
return t.Result;
}
But then when i call cancelSource.Cancel(), or after the 1000ms timer, it just hangs on Wait()... Why?

When you passing cancellation token to Task.Run and hit Cancel then, if task hasn't been started yet and pending on thread pool queue waiting to be executed, it will be removed from queue with state Canceled (read remarks). You can check it by scheduling a lot of tasks and observe that they immediately go to completed state as soon as you cancel them. If you don't pass cancellation token they all have to be executed. In addition, when task runs token, that you passed to Task.Run, has to be checked manually like ThrowIfCancellationRequested or passed to subsequent tasks to abort execution.
Passing cancellation token to Wait as mentioned in comments doesn't cancel task but works pretty much as timeout. If you hit cancel it just stops waiting it to be executed.

Related

How to stop current thread automatically(forcefully) in c# console Application [duplicate]

This question already has answers here:
How do I abort/cancel TPL Tasks?
(13 answers)
Closed 2 years ago.
Good day Everyone, I am new to multithreading coding and I have task to change the flow of currently working console application. Currently when console application start it goes through following code. "Task.Factory.Startnew" method start new thread and run "CheckDatabaseFieldStatus" which is checking status of Database table field. If this field has "cancelling" value then it will call Token.cancel() method.
Meanwhile, current thread is executing some sort of logic and keep calling "CheckCancelTokenStatus" function to throw "ThrowIfCancellationRequested" exception.
New requirement: I want to stop current thread from another thread which is created by "Task.Factory.Startnew" method. How can I achieve forceful cancellation of current thread from another thread in safe way?
Here is code:
public CancellationTokenSource TokenSource = new CancellationTokenSource();
public CancellationToken Token = TokenSource.Token;
try
{
// Spin up a task that will keep checking the request to see if it has been cancelled.
Task cancelCheck = Task.Factory.StartNew(CheckDatabaseFieldStatus, TaskCreationOptions.LongRunning);
//Some logic to finish task
CheckCancelTokenStatus(Token);
//Some logic to finish task
CheckCancelTokenStatus(Token);
//Some logic to finish task
CheckCancelTokenStatus(Token);
//Some logic to finish task
}
catch (OperationCanceledException){
//Database call to Update status of task to canceled
}
//here is dispose method dispose token
Here is function "CheckDatabaseFieldStatus"
public void CheckDatabaseFieldStatus()
{
// While a Request is running, we're going to keep polling to check if we want to cancel.
while (!Token.IsCancellationRequested)
{
// Using the request timer here for our wait would mean that requests could take up to 30
seconds to finish after they've actually finished.
Thread.Sleep(5000);
// Create a new context with each check to avoid conflicts with main context
using (DbContext mydb= new DbContext())
{
// Get the newest version of the request and check if it's set to cancel.
if (mydb.Table.GetAll().Any(r => r.Status=="cancelling"))
{
TokenSource.Cancel();
}
}
}
}
Here is function CheckCancelTokenStatus
function CheckCancelTokenStatus(CancellationToken Token)
{
if (Token.HasValue && Token.Value.IsCancellationRequested)
{
Token.Value.ThrowIfCancellationRequested();
}
}
Instead of using Thread.Sleep try use Task.Delay(int milliseconds, CancellationToken token). Task.Delay creates task that will complete after given delay. If token is cancelled during wait, it will throw exception and execution will stop. Also better to check before and after waiting for cancellation, because somebody could cancel execution while you were sleeping.
public void CheckDatabaseFieldStatus()
{
while (!Token.IsCancellationRequested)
{
Task.Delay(5000, Token).GetAwaiter().GetResult();
Token.ThrowIfCancellationRequested();
using (DbContext mydb= new DbContext())
{
if (mydb.Table.GetAll().Any(r => r.Status=="cancelling"))
{
TokenSource.Cancel();
}
}
}
}
Also I'm using GetAwaiter().GetResult() instead of simple Wait() because second option wraps exception. See this answer for explanation https://stackoverflow.com/a/38530225/10339675
In general it's better to use async/await instead of synchronously wait

Call async method with await in it from synchronous action method

I need to implement a service which fire a start of the processing. But I don't need to wait for result. I can just show the default output and in the background the process will be working.
However I came up to the problem that the code after await is not executed.
I prepared some code to show the idea:
public class HomeController : Controller
{
public ActionResult Deadlock()
{
AsyncCall();
return View();
}
private async Task AsyncCall()
{
await Task.Delay(10000);
var nonreachablePlace = "The breakpoint will not set the execution here";
Do(nonreachablePlace);
}
private void Do(string m)
{
m.Contains("x");
}
}
I know that it looks very bad. But the idea was like:
A thread go to Deadlock method.
The thread go to AsyncCall method synchronously.
Faces the await statement.
Go from the Deadlock method.
Continue main method to the end.
When Task.Delay finished, this thread will come up from the thread pool and continue working.
To my bad 6 step is not processed. I have tried to set up the breakpoint and never got hit.
But if I reduce the time delay and do it in debug I will come to the 6 step.
Enter to the controller's action method
After return from controller's action method
But if I leave only one breakpoint after await, I won't go to the 6 step
var nonreachablePlace = "The breakpoint will not set the execution here";
NOTE:
I append ConfigureAwait(false) to the Task.Delay(). It looks like this:
private async Task AsyncCall()
{
await Task.Delay(10000).ConfigureAwait(false);
var nonreachablePlace = "The breakpoint will not set the execution here";
Do(nonreachablePlace);
}
And now it works as expected.
My question is why does the code not work without ConfigureAwait(false)?
Maybe it somehow related to SynchronizationContext and it is not reachable after the main thread finishes its work. And after that awaitable method tryes to get a context when it has been already disposed (Just my thought)
Use HostingEnvironment.QueueBackgroundWorkItem.
Note this is only available in Classic ASP.NET on .NET Framework (System.Web.dll) and not ASP.NET Core (I forget to what extent it works in ASP.NET Core 1.x and 2.x running on .NET Framework, but anyway).
All you need is this:
using System.Web.Hosting;
public class MyController : Controller
{
[HttpPost( "/foo" )]
public async Task<ActionResult> DoSomething()
{
HostingEnvironment.QueueBackgroundWorkItem( this.DoSomethingExpensiveAsync ); // Pass the method by name or as a `Func<CancellationToken,Task>` delegate.
return this.View();
}
private async Task DoSomethingExpensiveAsync( CancellationToken cancellationToken )
{
await Task.Delay( TimeSpan.FromSeconds( 30 ) );
}
}
You can also use it with non-async workloads:
[HttpPost( "/foo" )]
public async Task<ActionResult> DoSomething()
{
HostingEnvironment.QueueBackgroundWorkItem( this.DoSomethingExpensive ); // Pass the method by name or as a `Action<CancellationToken>` delegate.
return this.View();
}
private void DoSomethingExpensive( CancellationToken cancellationToken )
{
Thread.Sleep( 30 * 1000 ); // NEVER EVER EVER call Thread.Sleep in ASP.NET!!! This is just an example!
}
If you want to start a job normally at first and only finish it in the background if it takes too long, then do this:
[HttpPost( "/foo" )]
public async Task<ActionResult> DoSomething()
{
Task<String> workTask = this.DoSomethingExpensiveAsync( default );
Task timeoutTask = Task.Delay( TimeSpan.FromSeconds( 5 ) );
Task first = await Task.WhenAny( workTask, timeoutTask );
if( first == timeoutTask )
{
// `workTask` is still running, so resume it in the background:
HostingEnvironment.QueueBackgroundWorkItem( async ct => await workTask );
return this.View( "Still working..." );
}
else
{
// `workTask` finished before the timeout:
String result = await workTask; // or just `workTask.Result`.
return this.View( result );
}
}
private async Task<String> DoSomethingExpensiveAsync( CancellationToken cancellationToken )
{
await Task.Delay( TimeSpan.FromSeconds( 30 ) );
return "Explosive bolts, ten thousand volts; At a million miles an hour, Abrasive wheels and molten metals";
}
So Deadlock() calls AsyncCall()
Then AsyncCall() Tells DeadLock() "Okay, well I'm waiting for Task.Delay to count to 10,000 but you can go ahead."
...so AsyncCall() yields the main thread back over to DeadLock().
Now DeadLock() never said anything about waiting on AsyncCall() to finish, so as far as DeadLock() is concerned, AsyncCall() has already returned (it actually only yielded, but the program cursor would still be passed back out into DeadLock().
So I would suggest setting your breakpoint at the AsyncCall() method in DeadLock(), because you'll probably see that your main thread is already done and exited before the Task.Delay() is even done.
So AsyncCall() never even gets a chance to finish Awaiting.
I dived deep into the logic behind Task.Delay(10000) and continuation code after that.
Thanks to the post made by Stephen Toub.
The main problem was in part when a Task had finished. And it's result needed to be processed by next thread.
Since I hadn't written ConfigureAwait() I implicitly meant to run the code in a thread which has SynchronizationContext (AspNetSynchronizationContext in my case).
private async Task AsyncCall()
{
/// The delay is done by a thread from a ThreadPool.
await Task.Delay(10000);
/// After the Task has been finished
/// TaskAwaiter tryies to send a continuation code to a thread with
/// SynchronizationContext.
var nonreachablePlace = "The breakpoint will not set the execution here";
Do(nonreachablePlace);
}
Because I hadn't wanted to wait the result of awaitable, I returned the response from controller's action. Then the thread went to ThreadPool and SynchronizationContext was disposed.
To the moment of Task completion, there was no SynchronizationContext to send a delegate with continuation code.
And during the code creation Visual Studio Enabled Just My Code option was set to true. That's why this exception was thrown silently to me.
And about the situation when I was able to run a code even I had Task.Delay(2000). I think it's caused by the time needed to Classic ASP.NET to complete a request and create a response to it. During this time you can get a reference to SynchronizationContext and Post a delegate to it.

Is Task.ContinueWith() guaranteed to run?

Suppose I have some async task that can sometimes run fast and sometimes slow,
public Random seed = new Random();
private async Task<string> _Work()
{
int time = seed.Next(0, 5000);
string result = string.Format("Worked for {0} milliseconds", time);
await Task.Delay(time);
return result;
}
public void SomeMethod()
{
_Work(); // starts immediately? Am I right?
// since _Work() will be executed immediately before ContinueWith() is executed,
// will there be a chance that callback will not be called if _Work completes very quickly,
// like before ContinueWith() can be scheduled?
_Work().ContinueWith(callback)
}
Is the callback in Task.ContinueWith() guaranteed to run in the above scenario?
will there be a chance that callback will not be called if _Work completes very quickly?
No. Continuations passed to ContinueWith will always be scheduled. If the task is already complete, they will be scheduled immediately. The task uses a thread-safe kind of "gate" to ensure that a continuation passed to ContinueWith will always be scheduled; there is a race condition (of course) but it's properly handled so that the continuation is always scheduled regardless of the results of the race.

How to wait for Task to start?

How can I wait for the task to start. The following code fails:
var asyncmethod = ...a Task<TReturn>, with .Start() called on it...;
int waitcounter = 0;
while (!asyncmethod.Wait(1000))
{
waitcounter++;
Log("waiting very long...");
}
ret = asyncmethod.Result;
The asyncmethod.Wait(1000) waits 1 seconds as expected, but the Task is in the state WaitingToRun and will never start running when Wait()ing. On the other hand, when .Result is called, it will start running. How to get it to run without calling .Result?
the Task is in the state WaitingToRun and will never start running when Wait()ing
When a task is in the WaitingToRun state, that means it is ready to start running and is just waiting for its scheduling context to be available, so it can be scheduled and run (as I describe on my blog).
Since the task is still in this state after Wait(1000) finishes, then presumably the task is waiting for the scheduling context that is used by the calling thread, and thus cannot be scheduled until that thread is free.
Task.Result can trigger task inlining and execute task, but apparently Wait() cannot.
Both .Result and .Wait() will permit the task to be inlined, but of course .Wait(x) cannot because it has to honor the timeout.
However, neither .Result nor .Wait() will guarantee inlining - and it's important to keep that in mind when writing reliable code.
the code shouldn't break, regardless of if the task is scheduled on the "current" or a separate thread.
That's an extremely difficult requirement to satisfy. Are you sure you need that?
The easiest solution would be to wait asynchronously:
Task<T> asyncmethod = ...;
int waitcounter = 0;
while (await Task.WhenAny(Task.Delay(1000), asyncmethod) != asyncmethod)
{
waitcounter++;
Log("waiting very long...");
}
ret = await asyncmethod;
Just wait for the task to be completed using:
asyncmethod.Start();
asyncmethod.Wait(); // not needed in most cases
// but if used, the task is completed at this point.
var ret = asyncmethod.Result; // automatically waits for the task to be completed
but basically, the waiting is not neccesary, unless you have a reason for this. From the Task<TResult>.Result-docs:
The get accessor for this property ensures that the asynchronous
operation is complete before returning. Once the result of the
computation is available, it is stored and will be returned
immediately on later calls to Result. (from msdn)
Not really sure why you're doing this, but this can be achieved without blocking the calling thread using Task.IsCompleted and Task.Delay:
public async Task FooAsync()
{
var waitCounter = -1;
var task = Task.Run(() => { });
do
{
waitCounter++;
await Task.Delay(1000);
}
while (!task.IsCompleted)
}
This snippet will call Log a single time if the Task takes more than 1000ms to complete.
private async static void StartTask()
{
Task<object> asyncmethod = ... ;
LogDurationTooLong(asyncmethod, 1000);
var result = await asyncmethod;
}
/// <summary>
/// Logs if a task takes too long to complete.
/// </summary>
/// <param name="asyncmethod">The task to reference.</param>
/// <param name="duration">The duration after which a log entry is made.</param>
private async static void LogDurationTooLong(Task asyncmethod, int duration)
{
Task completedTask = await Task.WhenAny(Task.Delay(duration), asyncmethod);
if (completedTask != asyncmethod)
{
Log("waiting very long...");
}
}

Proper way to implement a never ending task. (Timers vs Task)

So, my app needs to perform an action almost continuously (with a pause of 10 seconds or so between each run) for as long as the app is running or a cancellation is requested. The work it needs to do has the possibility of taking up to 30 seconds.
Is it better to use a System.Timers.Timer and use AutoReset to make sure it doesn't perform the action before the previous "tick" has completed.
Or should I use a general Task in LongRunning mode with a cancellation token, and have a regular infinite while loop inside it calling the action doing the work with a 10 second Thread.Sleep between calls? As for the async/await model, I'm not sure it would be appropriate here as I don't have any return values from the work.
CancellationTokenSource wtoken;
Task task;
void StopWork()
{
wtoken.Cancel();
try
{
task.Wait();
} catch(AggregateException) { }
}
void StartWork()
{
wtoken = new CancellationTokenSource();
task = Task.Factory.StartNew(() =>
{
while (true)
{
wtoken.Token.ThrowIfCancellationRequested();
DoWork();
Thread.Sleep(10000);
}
}, wtoken, TaskCreationOptions.LongRunning);
}
void DoWork()
{
// Some work that takes up to 30 seconds but isn't returning anything.
}
or just use a simple timer while using its AutoReset property, and call .Stop() to cancel it?
I'd use TPL Dataflow for this (since you're using .NET 4.5 and it uses Task internally). You can easily create an ActionBlock<TInput> which posts items to itself after it's processed it's action and waited an appropriate amount of time.
First, create a factory that will create your never-ending task:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Action<DateTimeOffset> action, CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action.
action(now);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
I've chosen the ActionBlock<TInput> to take a DateTimeOffset structure; you have to pass a type parameter, and it might as well pass some useful state (you can change the nature of the state, if you want).
Also, note that the ActionBlock<TInput> by default processes only one item at a time, so you're guaranteed that only one action will be processed (meaning, you won't have to deal with reentrancy when it calls the Post extension method back on itself).
I've also passed the CancellationToken structure to both the constructor of the ActionBlock<TInput> and to the Task.Delay method call; if the process is cancelled, the cancellation will take place at the first possible opportunity.
From there, it's an easy refactoring of your code to store the ITargetBlock<DateTimeoffset> interface implemented by ActionBlock<TInput> (this is the higher-level abstraction representing blocks that are consumers, and you want to be able to trigger the consumption through a call to the Post extension method):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
Your StartWork method:
void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();
// Set the task.
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now);
}
And then your StopWork method:
void StopWork()
{
// CancellationTokenSource implements IDisposable.
using (wtoken)
{
// Cancel. This will cancel the task.
wtoken.Cancel();
}
// Set everything to null, since the references
// are on the class level and keeping them around
// is holding onto invalid state.
wtoken = null;
task = null;
}
Why would you want to use TPL Dataflow here? A few reasons:
Separation of concerns
The CreateNeverEndingTask method is now a factory that creates your "service" so to speak. You control when it starts and stops, and it's completely self-contained. You don't have to interweave state control of the timer with other aspects of your code. You simply create the block, start it, and stop it when you're done.
More efficient use of threads/tasks/resources
The default scheduler for the blocks in TPL data flow is the same for a Task, which is the thread pool. By using the ActionBlock<TInput> to process your action, as well as a call to Task.Delay, you're yielding control of the thread that you were using when you're not actually doing anything. Granted, this actually leads to some overhead when you spawn up the new Task that will process the continuation, but that should be small, considering you aren't processing this in a tight loop (you're waiting ten seconds between invocations).
If the DoWork function actually can be made awaitable (namely, in that it returns a Task), then you can (possibly) optimize this even more by tweaking the factory method above to take a Func<DateTimeOffset, CancellationToken, Task> instead of an Action<DateTimeOffset>, like so:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action. Wait on the result.
await action(now, cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Same as above.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
Of course, it would be good practice to weave the CancellationToken through to your method (if it accepts one), which is done here.
That means you would then have a DoWorkAsync method with the following signature:
Task DoWorkAsync(CancellationToken cancellationToken);
You'd have to change (only slightly, and you're not bleeding out separation of concerns here) the StartWork method to account for the new signature passed to the CreateNeverEndingTask method, like so:
void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();
// Set the task.
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now, wtoken.Token);
}
I find the new Task-based interface to be very simple for doing things like this - even easier than using the Timer class.
There are some small adjustments you can make to your example. Instead of:
task = Task.Factory.StartNew(() =>
{
while (true)
{
wtoken.Token.ThrowIfCancellationRequested();
DoWork();
Thread.Sleep(10000);
}
}, wtoken, TaskCreationOptions.LongRunning);
You can do this:
task = Task.Run(async () => // <- marked async
{
while (true)
{
DoWork();
await Task.Delay(10000, wtoken.Token); // <- await with cancellation
}
}, wtoken.Token);
This way the cancellation will happen instantaneously if inside the Task.Delay, rather than having to wait for the Thread.Sleep to finish.
Also, using Task.Delay over Thread.Sleep means you aren't tying up a thread doing nothing for the duration of the sleep.
If you're able, you can also make DoWork() accept a cancellation token, and the cancellation will be much more responsive.
Here is what I came up with:
Inherit from NeverEndingTask and override the ExecutionCore method with the work you want to do.
Changing ExecutionLoopDelayMs allows you to adjust the time between loops e.g. if you wanted to use a backoff algorithm.
Start/Stop provide a synchronous interface to start/stop task.
LongRunning means you will get one dedicated thread per NeverEndingTask.
This class does not allocate memory in a loop unlike the ActionBlock based solution above.
The code below is sketch, not necessarily production code :)
:
public abstract class NeverEndingTask
{
// Using a CTS allows NeverEndingTask to "cancel itself"
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
protected NeverEndingTask()
{
TheNeverEndingTask = new Task(
() =>
{
// Wait to see if we get cancelled...
while (!_cts.Token.WaitHandle.WaitOne(ExecutionLoopDelayMs))
{
// Otherwise execute our code...
ExecutionCore(_cts.Token);
}
// If we were cancelled, use the idiomatic way to terminate task
_cts.Token.ThrowIfCancellationRequested();
},
_cts.Token,
TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning);
// Do not forget to observe faulted tasks - for NeverEndingTask faults are probably never desirable
TheNeverEndingTask.ContinueWith(x =>
{
Trace.TraceError(x.Exception.InnerException.Message);
// Log/Fire Events etc.
}, TaskContinuationOptions.OnlyOnFaulted);
}
protected readonly int ExecutionLoopDelayMs = 0;
protected Task TheNeverEndingTask;
public void Start()
{
// Should throw if you try to start twice...
TheNeverEndingTask.Start();
}
protected abstract void ExecutionCore(CancellationToken cancellationToken);
public void Stop()
{
// This code should be reentrant...
_cts.Cancel();
TheNeverEndingTask.Wait();
}
}

Categories

Resources