Does overloaded version of Task.WaitAny Cancel the task? - c#

int WaitAny(Task[] tasks, int millisecondsTimeout);
Does the above method cancel the task after the timeout period? It looks it doesn't but C# 70-483 exam reference book says this overloaded version cancel the task. Here is some test code,
Task longRunning = Task.Run(() =>
{
Thread.Sleep(10000);
});
int index = Task.WaitAny(new Task[] { longRunning }, 1000); // returns -1
index = 0; // reset it to reuse in the loop
while (index < 12)
{
Thread.Sleep(1000);
index++;
Console.WriteLine("long running task status {0}", longRunning.Status);
}
First few times, Status is Running and after that the status change to RanToCompletion. So what that time out does related to Task and WaitAny?

Task.WaitAny will complete if either (a) any of tasks it wraps complete or (b) the timeout is exceeded. If the timeout is exceeded, WaitAny completes, but the tasks it wraps will not be cancelled. Cancellation with tasks tends to be cooperative rather than implicit, and will involve the explicit passing of a CancellationToken.
If you want the task to cancel after a timeout you can create a CancellationTokenSource with a timeout and pass that into the task you're waiting for:
using(CancellationTokenSource cts=new CancellationTokenSource(TimeSpan.FromSeconds(5)))
{
var task = Task.Delay(TimeSpan.FromSeconds(10), cts.Token);
task.Wait();
}
Of course, waiting for tasks to complete using blocking methods is highly discouraged, as is wrapping synchronous blocking code in Task.Run and expecting everything to work correctly and "asynchronously".
Embrace async/await for the win.

If you look here: https://msdn.microsoft.com/en-us/library/dd235650(v=vs.110).aspx
IT says waitAny returns -1 if timeout has occurred. which is the result you are getting in the first place. Isn't that your expectation?
This means the code continues past WaitAny but does not mean that the tasks it was waiting for have been cancelled.

So what that time out does related to Task and WaitAny?
It does nothing. When you set time-out of 10000 it's like if you say:
"Hey Task, i give you ten seconds to finish, but then i'll keep going regardless of what's going on..."

Related

Does or does Task.Wait not start the task if it isn't already running?

According to the Microsoft TPL documentation I read (link) calling the Task.Wait() method will block the current thread until that task finishes (or cancels, or faults). But it also said that if the task in question hadn't started yet, the Wait method will attempt to run it on its own thread by asking the scheduler to reassign it, thus reducing the amount of wastage due to blocking.
I've got a system in which tasks (once running) begin by collecting data by starting other tasks and waiting on their results. These other tasks in turn begin by collecting data from yet other tasks and-so-on-and-so-fort, potentially a few hundred layers deep. I really don't want umpteen tasks blocking and waiting for the one task at the end to finally finish.
However when I tried this out in a test console app, Task.Wait() doesn't seem to start anything at all.
What are the correct incantations for building a sequence of tasks that must all wait on each other with a minimum of wasted cycles? It's sort of like ContinueWith, except starting with the last task in the series...
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var source = new CancellationTokenSource();
var token = source.Token;
// Create a non-running task.
var task = new Task<string[]>(() => InternalCompute(token), token);
// Isn't this supposed to start the task?
task.Wait(CancellationToken.None);
// I realise this code now won't run until the task finishes,
// it's here for when I use task.Start() instead of task.Wait().
Console.WriteLine("Press any key to cancel the process.");
Console.ReadKey(true);
source.Cancel();
Console.WriteLine("Source cancelled...");
Console.WriteLine("Press any key to quit.");
Console.ReadKey(true);
}
private static string[] InternalCompute(CancellationToken token)
{
string[] data;
try
{
data = Compute(token);
}
catch (TaskCanceledException ex)
{
return null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new[] { ex.Message };
}
Console.WriteLine("Post-processor starting.");
for (int i = 0; i < data.Length; i++)
if (data[i] is null)
Console.WriteLine($"Null data at {i}.");
else
Console.WriteLine($"Valid data at {i}.");
Console.WriteLine("Post-processor completed.");
return data;
}
/// <summary>
/// This method stands in for an abstract one to be implemented by plug-in developers.
/// </summary>
private static string[] Compute(CancellationToken token)
{
var data = new string[10];
for (int i = 0; i < data.Length; i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(250);
data[i] = i.ToString();
Console.WriteLine($"Computing item {i + 1}...");
}
return data;
}
}
}
Tasks are generally split into two groups - "cold" tasks and "hot" tasks. "cold" tasks are tasks that have not yet been started and are not meant to run yet. "hot" tasks are tasks that may or may not be currently running but, importantly, if they're not running yet, they may do so at any time. They're meant to be running but haven't yet been assigned the resource (a thread) they need to do so.
What this post is talking about is executing a "hot" task that hasn't otherwise had an opportunity to run. "hot" tasks are created by calling e.g. Task.Run(). They're also e.g. the type of Tasks you'll receive from async methods. new Task(...), on the other hand gives you "cold" tasks. Unless or until you call Start or moral equivalent methods on that task, it remains "cold". It's calling one of those methods explicitly that makes it "hot" instead of "cold".
Generally, you don't want to be working with "cold" tasks at all these days, which is why directly calling a Task constructor is frowned upon. They were really a bad experiment from before they worked out how scheduling should really work. Most modern code doesn't expect to be working with "cold" tasks at all.
The key quote from the above post is this one:
However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
If you've not called Start on the task, it hasn't been queued with a scheduler - so obviously we cannot do what the above says.
This is the part of the article that caused the confusion (emphasis added).
If the Task being Wait’d on has already started execution, Wait has to block. However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
This is the description of the Task.Start method:
Starts the Task, scheduling it for execution to the current TaskScheduler.
And these are the eight different stages of a task's lifecycle:
public enum TaskStatus
{
Created = 0, // The task has been initialized but has not yet been scheduled.
WaitingForActivation = 1, // The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
WaitingToRun = 2, // The task has been scheduled for execution but has not yet begun executing.
Running = 3, // The task is running but has not yet completed.
WaitingForChildrenToComplete = 4, // The task has finished executing and is implicitly waiting for attached child tasks to complete.
RanToCompletion = 5, // The task completed execution successfully.
Canceled = 6, // The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken while the token was in signaled state, or the task's CancellationToken was already signaled before the task started executing.
Faulted = 7 // The task completed due to an unhandled exception.
}
The article is talking implicitly about hot tasks that are either in the WaitingForActivation or in the WaitingToRun stage, and explains under which conditions they could be progressed internally to the Running stage when their Wait method is called. It's not talking about cold tasks in the Created stage. A task in this stage cannot be progressed without either its Start or RunSynchronously method called. In other words the .NET infrastructure never changes the temperature of a task from cold to hot automatically. This responsibility belongs 100% to the application code that created the task using the Task constructor.
As a side note, it's worth quoting this sentence from the remarks:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.

CancellationTokenSource not working properly with Parallel.ForEach

I have the following code:
CancellationTokenSource ts = new CancellationTokenSource(10000);
ParallelOptions po = new ParallelOptions();
po.CancellationToken = ts.Token;
List<int> lItems = new List<int>();
for (int i = 0; i < 20; i++)
lItems.Add(i);
System.Collections.Concurrent.ConcurrentBag<int> lBgs = new System.Collections.Concurrent.ConcurrentBag<int>();
Stopwatch sp = Stopwatch.StartNew();
try
{
Parallel.ForEach(lItems, po, i =>
{
Task.Delay(i * 1000).Wait();
lBgs.Add(i);
});
}
catch (Exception ex)
{
}
Console.WriteLine("Elapsed time: {0:N2} seg Total items: {1}", sp.ElapsedMilliseconds / 1000.0, lBgs.Count);
My question is why takes more than 20 sec to cancel the operation (parallel for) if the CancelationTokenSource is set to finish in 10 sec
Regards
Without a good Minimal, Complete, and Verifiable code example, it's impossible to fully understand your scenario. But based on the code you posted, it appears that you expect for your CancellationToken to affect the execution of each individual iteration of the Parallel.ForEach().
However, that's not how it works. The Parallel.ForEach() method schedules individual operations concurrently, but once those operations start, they are out of the control of the Parallel.ForEach() method. If you want them to terminate early, you have to do that yourself. E.g.:
Parallel.ForEach(lItems, po, i =>
{
Task.Delay(i * 1000, ts.Token).Wait();
lBgs.Add(i);
});
As your code stands now, all 20 actions are started almost immediately (there's a short delay as the thread pool creates enough threads for all the actions, if necessary), before you cancel the token. That is, by the time you cancel the token, the Parallel.ForEach() method no longer has a way to avoid starting the actions; they are already started!
Since your individual actions don't do anything to interrupt themselves, then all that's left is for them all to complete. The start-up time (including waiting for the thread pool to create enough worker threads), plus the longest total delay (i.e. the delay to start an action plus that action's delay), determines the total time the operation takes, with your cancellation token having no effect. Since your longest action is 20 seconds, the total delay for the Parallel.ForEach() operation will always be at least 20 seconds.
By making the change I show above, the delay task for each individual action will be cancelled by your token when it expires, causing a task-cancelled exception. This will cause the action itself to terminate early as well.
Note that there is still value in assigning the cancellation token to the ParallelOptions.CancellationToken property. Even though the cancellation happens too late to stop Parallel.ForEach() from starting all of the actions, by providing the token in the options, it can recognize that the exception thrown by each action was caused by the same cancellation token used in the options. With that, it then can throw just a single OperationCanceledException, instead of wrapping all of the action exceptions in an AggregateException.
I am assuming you are not actually
In response to
My question is why takes more than 20 sec to cancel the operation (parallel for) if the CancelationTokenSource is set to finish in 10 sec
This happens because you are not cancelling the Parallel.ForEach
In order to actually cancel you need to to use
po.CancellationToken.ThrowIfCancellationRequested();
inside the Parallel.ForEach code
As previous answer pointed out, if you want to actually cancel the task created by Task.Delay() you need to use the overload of Task.Delay which accepts a CancellationToken
Task.Delay(i * 1000, po.CancellationToken).Wait();
public static Task Delay(
TimeSpan delay,
CancellationToken cancellationToken
)
More details here
MSDN How to: Cancel a Parallel.For or ForEach Loop

Usage of Task.WhenAll with infinite Tasks produced by BlockingCollection

I am adding Background Tasks to a Blocking Collection (added in the Background).
I am waiting with Task.WhenAll on a Enumerable returned by GetConsumingEnumerable.
My question is: Is the overload of Task.WhenAll which receives an IEnumerable "prepared" to potentially receive an endless amount of tasks?
I am simply not sure if i can do it this way or if it was meant to be used this way?
private async Task RunAsync(TimeSpan delay, CancellationToken cancellationToken)
{
using (BlockingCollection<Task> jobcollection = new BlockingCollection<Task>())
{
Task addingTask = Task.Run(async () =>
{
while (true)
{
DateTime utcNow = DateTime.UtcNow;
var jobs = Repository.GetAllJobs();
foreach (var job in GetRootJobsDue(jobs, utcNow))
{
jobcollection.Add(Task.Run(() => RunJob(job, jobs, cancellationToken, utcNow), cancellationToken), cancellationToken);
}
await Task.Delay(delay, cancellationToken);
}
}, cancellationToken);
await Task.WhenAll(jobcollection.GetConsumingEnumerable(cancellationToken));
}
}
Since your goal is merely to wait until the cancellation token is cancelled, you should do that. For reasons others have explained, using WhenAll on an infinite sequence of tasks isn't the way to go about that. There are easier ways to get a task that will never complete.
await new TaskCompletionSource<bool>().Task
.ContinueWith(t => { }, cancellationToken);
Task.WhenAll will not work with an infinite number of tasks. It will first (synchronously) wait for the enumerable to finish, and then (asynchronously) wait for them all to complete.
If you want to react to a sequence in an asynchronous way, then you need to use IObservable<Task> (Reactive Extensions). You can use a TPL Dataflow BufferBlock as a "queue" that can work with either synchronous or asynchronous code, and is easily convertible to IObservable<Task>.
I assume that Task.WhenAll will try to enumerate the collection, which means that it itself will block until the collection is completed or cancelled. If it didn't, then the code could theoretically finish awaiting before the tasks have been created. So there's going to be an extra block in there... it will block waiting for the threads to be created, and then block again until the tasks are done. I don't think that's a bad thing for your code, as it's still going to block until the same point in time.

Is Wait() necessary after using Task.Factory.StartNew()?

Almost all documentation that I have seen on using the C# 4.0 Task.Factory.StartNew states that in order to wait for the Task to complete, you need a Wait. But my initial testing shows that it is unnecessary. Can anyone else give me confirmation on this? I'm curious as to why so much online and printed references say you should call Wait.
Here's a simple console app that shows that I don't need the Wait statement, so I commented it out. Whether or not I comment out the tsk.Wait(), the output is the same.
Expected output in all cases is as follows:
Main thread starting.
After running MyTask. The result is True
After running SumIt. The result is 1
Main thread ending.
The code:
class Program
{
// A trivial method that returns a result and takes no arguments.
static bool MyTask()
{
Thread.Sleep(2000);
return true;
}
// This method returns the summation of a positive integer
// which is passed to it.
static int SumIt(object v)
{
int x = (int)v;
int sum = 0;
for (; x > 0; x--)
sum += x;
return sum;
}
static void Main(string[] args)
{
Console.WriteLine("Main thread starting.");
// Construct the first task.
Task<bool> tsk = Task<bool>.Factory.StartNew(() => MyTask());
// I found this Wait statement to be completely unnecessary.
//tsk.Wait();
Console.WriteLine("After running MyTask. The result is " +
tsk.Result);
// Construct the second task.
Task<int> tsk2 = Task<int>.Factory.StartNew(() => SumIt(1));
Console.WriteLine("After running SumIt. The result is " +
tsk2.Result);
tsk.Dispose();
tsk2.Dispose();
Console.WriteLine("Main thread ending.");
Console.ReadLine();
}
}
If you just want to wait for the task to finish, the recommended course of action is to call .Wait(). For a Task (as opposed to a Task<T>) this is the only option.
For a Task<T>, however, there is also .Result, which also waits, and that is what you are using. So in your case it is unnecessary to call .Wait().
One important feature of Wait is that it acts as a rendezvous point in that any exception thrown by the Task will be re-throw at this point. As the current Task implementation* forces you to observe any such exception Wait is a good option for doing so. You can, however, also observe the exception by querying the Task instance for an exception.
*) Apparently this will be changed. The behavior is changed in the Async CTP.
Since according to this, accessing the Value of the Task ensures that the task is completed, you're right that it's not required.
As Timwi stated, .Result also waits. Since you are using tsk.Result in your Console.WriteLine call, you are doing the wait as a side-effect.
It also depends on how long it takes the task to complete. If it is very short, you may not realize the need for .Wait, because it seems to always finish in time. There is danger in leaving it out if you need the task to complete before continuing. The .Wait should therefore be used even if 99% of the time, it doesn't actually result in any time being spent waiting.

Does Task.Wait(int) stop the task if the timeout elapses without the task finishing?

I have a task and I expect it to take under a second to run but if it takes longer than a few seconds I want to cancel the task.
For example:
Task t = new Task(() =>
{
while (true)
{
Thread.Sleep(500);
}
});
t.Start();
t.Wait(3000);
Notice that after 3000 milliseconds the wait expires. Was the task canceled when the timeout expired or is the task still running?
Task.Wait() waits up to specified period for task completion and returns whether the task completed in the specified amount of time (or earlier) or not. The task itself is not modified and does not rely on waiting.
Read nice series: Parallelism in .NET, Parallelism in .NET – Part 10, Cancellation in PLINQ and the Parallel class by Reed Copsey
And: .NET 4 Cancellation Framework / Parallel Programming: Task Cancellation
Check following code:
var cts = new CancellationTokenSource();
var newTask = Task.Factory.StartNew(state =>
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
}
token.ThrowIfCancellationRequested();
}, cts.Token, cts.Token);
if (!newTask.Wait(3000, cts.Token)) cts.Cancel();
If you want to cancel a Task, you should pass in a CancellationToken when you create the task. That will allow you to cancel the Task from the outside. You could tie cancellation to a timer if you want.
To create a Task with a Cancellation token see this example:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var t = Task.Factory.StartNew(() => {
// do some work
if (token.IsCancellationRequested) {
// Clean up as needed here ....
}
token.ThrowIfCancellationRequested();
}, token);
To cancel the Task call Cancel() on the tokenSource.
The task is still running until you explicitly tell it to stop or your loop finishes (which will never happen).
You can check the return value of Wait to see this:
(from http://msdn.microsoft.com/en-us/library/dd235606.aspx)
Return Value
Type: System.Boolean
true if the Task completed execution within the allotted time; otherwise, false.
Was the task canceled when the timeout expired or is the task still running?
No and Yes.
The timeout passed to Task.Wait is for the Wait, not the task.
If your task calls any synchronous method that does any kind of I/O or other unspecified action that takes time, then there is no general way to "cancel" it.
Depending on how you try to "cancel" it, one of the following may happen:
The operation actually gets canceled and the resource it works on is in a stable state (You were lucky!)
The operation actually gets canceled and the resource it works on is in an inconsistent state (potentially causing all sorts of problems later)
The operation continues and potentially interferes with whatever your other code is doing (potentially causing all sorts of problems later)
The operation fails or causes your process to crash.
You don't know what happens, because it is undocumented
There are valid scenarios where you can and probably should cancel a task using one of the generic methods described in the other answers. But if you are here because you want to interrupt a specific synchronous method, better see the documentation of that method to find out if there is a way to interrupt it, if it has a "timeout" parameter, or if there is an interruptible variation of it.

Categories

Resources