We have a project where we run some tasks in parallel. We are starting Tasks to do this. Every Task runs a code inside a CodeDom (written by customer to customize part of the process). It works fine.
I would like to know how to limitate the time of the execution of a Task. I am afraid of the user write a bad code and it result in something like infinity loop or a bad code which takes a lot of time to perform. I would like to give him 10 seconds to perform its code and after that, kill the thread that is running on the Task and finish the Task.
For sample (pseudo code):
Task[] tasks = GetListOfTasks();
foreach (var task in tasks)
{
task.Start();
}
I tried to do this:
Task.WaitAll(tasks, TimeSpan.FromSeconds(10));
I already have the cancelation token on all these tasks, but the user can't use it.
How can I stop the task execution and kill the tasks if it do not finish on the specified TimeSpan ?
Thank you.
var threadTask = Task.Run(() =>
{
// start thread
thread.Start();
// force to wait the thread
thread.Join();
});
// run a new task to wait the task/thread to finish in a timeout.
// on the continueWith, abort the thread.
Task.Run(() => Task.WaitAll(new[] { threadTask }, TimeSpan.FromSeconds(10)))
.ContinueWith(t =>
{
if (thread.IsAlive)
{
thread.Abort();
}
});
Related
I'm trying to understand what David Fowler said about Task.Factory.StartNew + TaskCreationOptions.LongRunning here.
💡 NOTE: Don't use TaskCreationOptions.LongRunning with async code as this will create a new thread which will be destroyed after first await.
I know that there is no point of having Task.Run or Task.Factory.StartNew in this case because SendLoopAsync and ReceiveLoopAsync are completely async. I also know that if there is a time-consuming synchronous part inside either one of these methods, the Task.Run/Task.Factory.StartNew should be inside that method.
What does David Fowler mean in his statement? That there shouldn't be TaskCreationOptions.LongRunning from within an async task? Or he meant that SendLoopAsync/ReceiveLoopAsync should not be async? I also know that TaskCreationOptions.LongRunning means that the task will start immediately, which isn't the case with just a normal task which gets scheduled by the scheduler, and might take some time to wind up. You can notice this behavior when starting multiple connections concurrently, which caused the Send and Receive loop to start with a significant delay.
public async Task StartAsync(CancellationToken cancellationToken)
{
_ = Task.Factory.StartNew(_ => SendLoopAsync(cancellationToken), TaskCreationOptions.LongRunning, cancellationToken);
_ = Task.Factory.StartNew(_ => ReceiveLoopAsync(cancellationToken), TaskCreationOptions.LongRunning, cancellationToken);
}
private async Task SendLoopAsync()
{
await foreach (var message in _outputChannel.Reader.ReadAllAsync(_cancellationSource?.Token))
{
if (_clientWebSocket.State == WebSocketState.Open)
{
await _clientWebSocket.SendAsync(message.Data.AsMemory(), message.MessageType, true, CancellationToken.None).ConfigureAwait(false);
}
}
}
David Fowler means that the SendLoopAsync/ReceiveLoopAsync should not be async. There is no point at starting a task as LongRunning, if this task is going to use the starting thread for a duration measured in nanoseconds. The ThreadPool was invented in order to handle exactly these types of situations. In case the ThreadPool is not responsive enough because it has become saturated, then it's more logical to try to find the cause of the saturation and fix it, instead of bypassing the ThreadPool, and creating new threads every time you have some microseconds-worth of work to do.
Here is a demonstration of what happens when LongRunning is combined with async:
var stopwatch = Stopwatch.StartNew();
Thread workerThread = null;
List<(string, long, System.Threading.ThreadState)> entries = new();
Task<Task> taskTask = Task.Factory.StartNew(async () =>
{
workerThread = Thread.CurrentThread;
entries.Add(("A", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
await Task.Delay(500);
entries.Add(("D", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
}, default, TaskCreationOptions.LongRunning, TaskScheduler.Default);
taskTask.Wait();
entries.Add(("B", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
workerThread.Join();
entries.Add(("C", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
await taskTask.Unwrap();
entries.Add(("E", stopwatch.ElapsedMilliseconds, workerThread.ThreadState));
foreach (var (title, elapsed, state) in entries)
Console.WriteLine($"{title } after {elapsed,3} msec worker thread is {state}");
Output:
A after 2 msec worker thread is Background
B after 6 msec worker thread is Background, Stopped
C after 6 msec worker thread is Stopped
D after 507 msec worker thread is Stopped
E after 507 msec worker thread is Stopped
Try it on Fiddle.
The lifetime of the worker thread was at most 6 milliseconds. All it really had to do was to instantiate an async state machine, and schedule a callback using a System.Threading.Timer component. 6 milliseconds look to me like an eon for such a minuscule workload. Most probably these 6 milliseconds were spent for inter-thread communication, and for the thread's creation and destruction.
I am running an infinite loop of updates using Task.Run. This is the code:
Task.Run( async () => {
while(true){
Thread.Sleep(10000);
checkForUpdates();
}
});
In the old days, I used to write special code for long running tasks. Does the duration of of a task execution matter with Task.Run? Is there a timeout built in?
No ! there is no built-in timeout for Task.Run(). That means it will continue running until you manually cancel it. And you can implement that by doing something like this.
int timeout = 1000;
var task = SomeOperationAsync(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await task;
}
else
{
// timeout/cancellation logic
}
you can checkout this blog for more understanding Crafting a task time-out
I know there are a million questions on this and I am trying their solutions but it does not seem to behave as I would expect.
With the following code:
await Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");
I get the output:
[ 5 seconds elapses ]
I'm running after Thread.Sleep
I'm running after Task.Run
Which is the same output I would get without using Task.Run
What I want is the output:
I'm running after Task.Run
[ 5 seconds elapses ]
I'm running after Thread.Sleep
Where execution of the 'parent' thread continues and the long-running task is executed 'in the background' in a way which does not affect the 'parent' thread.
As mentioned you need to remove the await. However that turns it into a fire-and-forget task which, if the above code just happens to be in say a console app's Main then your process could terminate before the task has completed.
Good:
// C# 7
static async void Main(string[] args) // <--- note `async` signature
{
await Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running
after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");
}
Bad: - fire-and-forget task that isn't awaited
static void Main(string[] args)
{
Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("Spider Man: I don't wan't to go!"); }); // POOF!!
Console.WriteLine("I'm running after Task.Run");
// sorry Spidy, you're dead
}
So you might not see I'm running after Thread.Sleep at all even if you have a Thread.Sleep(). (by the way you should be using Task.Delay()). You could mitigate it with a Console.ReadKey() but that might defeat the purpose.
Fire-and-forget tasks have their uses, and can be used reasonably safely in other types of long-running apps like say WinForms but people generally don't go spawning Tasks in the process's entry point so the tasks generally run to completion without any early termination by the process. However, it's generally a good idea to use await where you can so you don't shoot yourself in the foot.
Also, your requirements of wanting things in a certain order sort of demonstrates a misunderstanding of how async/await works.
One thing to keep in mind when working with await is that your code awaits objects. These objects are just like any other objects. Specifically, they can be assigned to local variables or class variables.
So, this code:
await Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");
is essentially the same as this code:
var task = Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
await task;
Console.WriteLine("I'm running after Task.Run");
In other words:
Start a task running on a background thread.
Asynchronously wait (await) for that task to complete.
Display "I'm running after Task.Run".
So the output is expected.
What I want is the output:
I'm running after Task.Run
[ 5 seconds elapses ]
I'm running after Thread.Sleep
In that case, start the task running on the background thread, but don't await it until later:
var task = Task.Run(() => { Thread.Sleep(5000); Console.WriteLine("I'm running after Thread.Sleep"); });
Console.WriteLine("I'm running after Task.Run");
await task;
If you want to run asynchronously you should remove the command away
static void Main(string[] args)
{
Task.Run(() => { Thread.Sleep(5000); Console.WriteLine(DateTime.Now + " => I'm running after Thread.Sleep"); });
Console.WriteLine(DateTime.Now + " => I'm running after Task.Run");
while (true)
{
Thread.Sleep(500);
}
}
Output
28/05/2019 09:49:58 => I'm running after Task.Run
28/05/2019 09:50:03 => I'm running after Thread.Sleep
Task.WhenAll(IEnumerable<Task>) waits for all tasks in the IEnumerable are complete --- but only the tasks in the list when it's first called. If any active task adds to the list, they aren't considered. This short example demonstrates:
List<Task> _tasks = new List<Task>();
public async Task QuickExample()
{
for(int n =0; n < 6; ++n)
_tasks.Add(Func1(n));
await Task.WhenAll(_tasks);
Console.WriteLine("Some Tasks complete");
await Task.WhenAll(_tasks);
Console.WriteLine("All Tasks complete");
}
async Task Func1(int n)
{
Console.WriteLine($"Func1-{n} started");
await Task.Delay(2000);
if ((n % 3) == 1)
_tasks.Add(Func2(n));
Console.WriteLine($"Func1-{n} complete");
}
async Task Func2(int n)
{
Console.WriteLine($"Func2-{n} started");
await Task.Delay(2000);
Console.WriteLine($"Func2-{n} complete");
}
This outputs:
Func1-0 started
Func1-1 started
Func1-2 started
Func1-3 started
Func1-4 started
Func1-5 started
Func1-5 complete
Func1-3 complete
Func2-1 started
Func1-1 complete
Func1-0 complete
Func1-2 complete
Func2-4 started
Func1-4 complete
Some Tasks complete
Func2-4 complete
Func2-1 complete
All Tasks complete
Done
The second Task.WhenAll() solves the problem in this case, but that's a rather fragile solution. What's the best way to handle this in the general case?
You are modifying the List<> without locking it... You like to live a dangerous life :-) Save the Count of the _tasks before doing a WaitAll, then after the WaitAll check the Count of _tasks. If it is different, do another round (so you need a while around the WaitAll.
int count = _tasks.Count;
while (true)
{
await Task.WhenAll(_tasks);
lock (_tasks)
{
if (count == _tasks.Count)
{
Console.WriteLine("All Tasks complete");
break;
}
count = _tasks.Count;
Console.WriteLine("Some Tasks complete");
}
}
async Task Func1(int n)
{
Console.WriteLine($"Func1-{n} started");
await Task.Delay(2000);
if ((n % 3) == 1)
{
lock (_tasks)
{
_tasks.Add(Func2(n));
}
}
Console.WriteLine($"Func1-{n} complete");
}
I'll add a second (probably more correct solution), that is different from what you are doing: you could simply await the new Tasks from the Tasks that generated them, without cascading them to the _tasks collection. If A creates B, then A doesn't finish until B finishes. Clearly you don't need to add the new Tasks to the _tasks collection.
Asynchronous function will return to the caller on first await.
So for loop will be complete before you add extra tasks to original tasks list.
Implementation of Task.WhenAll will iterate/copy tasks to local list, so added tasks after Task.WhenAll called will be ignored.
In your particular case moving call to Func1 before await Task.Delay() could be a solution.
async Task Func1(int n)
{
Console.WriteLine($"Func1-{n} started");
if ((n % 3) == 1)
_tasks.Add(Func2(n));
await Task.Delay(2000);
Console.WriteLine($"Func1-{n} complete");
}
But if in real scenario calling of Func2 depend on result of some asynchronous method, then you need some other solution.
Since it seems that additional tasks can be created during the course of executing the original list of tasks, you will need a simple while construct.
while (_tasks.Any( t => !t.IsCompleted )
{
await Task.WhenAll(_tasks);
}
This will check the list for any uncompleted tasks and await them until it catches the list at a moment when there are no tasks left.
Consider this; it sounds like work is being submitted to the "Task List" from another thread. In a sense, the "task submission" thread itself could also be yet another Task for you to wait on.
If you wait for all Tasks to be submitted, then you are guaranteed that your next call to WhenAll will yield a fully-completed payload.
Your waiting function could/should be a two-step process:
Wait for the "Task Submitting" task to complete, signalling all Tasks are submitted
Wait for all the submitted tasks to complete.
Example:
public async Task WaitForAllSubmittedTasks()
{
// Work is being submitted in a background thread;
// Wrap that thread in a Task, and wait for it to complete.
var workScheduler = GetWorkScheduler();
await workScheduler;
// All tasks submitted!
// Now we get the completed list of all submitted tasks.
// It's important to note: the submitted tasks
// have been chugging along all this time.
// By the time we get here, there are probably a number of
// completed tasks already. It does not delay the speed
// or execution of our work items if we grab the List
// after some of the work has been completed.
//
// It's entirely possible that - by the time we call
// this function and wait on it - almost all the
// tasks have already been completed!
var submittedWork = GetAllSubmittedTasks();
await Task.WhenAll(submittedWork);
// Work complete!
}
here is sample code for starting multiple task
Task.Factory.StartNew(() =>
{
//foreach (KeyValuePair<string, string> entry in dicList)
Parallel.ForEach(dicList,
entry =>
{
//create and add the Progress in UI thread
var ucProgress = (Progress)fpPanel.Invoke(createProgress, entry);
//execute ucProgress.Process(); in non-UI thread in parallel.
//the .Process(); must update UI by using *Invoke
ucProgress.Process();
System.Threading.Thread.SpinWait(5000000);
});
});
.ContinueWith(task =>
{
//to handle exceptions use task.Exception member
var progressBar = (ProgressBar)task.AsyncState;
if (!task.IsCancelled)
{
//hide progress bar here and reset pb.Value = 0
}
},
TaskScheduler.FromCurrentSynchronizationContext() //update UI from UI thread
);
when we start multiple task using Task.Factory.StartNew() then we can use .ContinueWith() block to determine when each task finish. i mean ContinueWith block fire once for each task completion. so i just want to know is there any mechanism in TPL library. if i start 10 task using Task.Factory.StartNew() so how do i notify after when 10 task will be finish. please give some insight with sample code.
if i start 10 task using Task.Factory.StartNew() so how do i notify after when 10 task will be finish
Three options:
The blocking Task.WaitAll call, which only returns when all the given tasks have completed
The async Task.WhenAll call, which returns a task which completes when all the given tasks have completed. (Introduced in .NET 4.5.)
TaskFactory.ContinueWhenAll, which adds a continuation task which will run when all the given tasks have completed.
if i start 10 task using Task.Factory.StartNew() so how do i notify after when 10 task will be finish
You can use Task.WaitAll. This call will block current thread until all tasks are finished.
Side note: you seem to be using Task, Parallel and Thread.SpinWait, which makes your code complex. I would spend a bit of time analysing if that complexity is really necessary.
You can use the WaitAll(). Example :
Func<bool> DummyMethod = () =>{
// When ready, send back complete!
return true;
};
// Create list of tasks
System.Threading.Tasks.Task<bool>[] tasks = new System.Threading.Tasks.Task<bool>[2];
// First task
var firstTask = System.Threading.Tasks.Task.Factory.StartNew(() => DummyMethod(), TaskCreationOptions.LongRunning);
tasks[0] = firstTask;
// Second task
var secondTask = System.Threading.Tasks.Task.Factory.StartNew(() => DummyMethod(), TaskCreationOptions.LongRunning);
tasks[1] = secondTask;
// Launch all
System.Threading.Tasks.Task.WaitAll(tasks);
Another solution:
After the completion of all the operation inside Parallel.For(...) it return an onject of ParallelLoopResult, Documentation:
For returns a System.Threading.Tasks.ParallelLoopResult object when
all threads have completed. This return value is useful when you are
stopping or breaking loop iteration manually, because the
ParallelLoopResult stores information such as the last iteration that
ran to completion. If one or more exceptions occur on one of the
threads, a System.AggregateException will be thrown.
The ParallelLoopResult class has a IsCompleted property that is set to false when a Stop() of Break() method has been executed.
Example:
ParallelLoopResult result = Parallel.For(...);
if (result.IsCompleted)
{
//Start another task
}
Note that it advised to use it only when breaking or stoping the loop manually (otherwise just use WaitAll, WhenAll etc).