Task.Run takes much time to start the task - c#

In my program i have ~40 running task, defined like:
private void StartTryReconnectTask() {
TryReconnectCTKS = new CancellationTokenSource();
TryReconnectTask = new Task(this.TryReconnect, TryReconnectCTKS.Token);
TryReconnectTask.Start();
}
Inside TryReconnect() there is an infinite while loop that stops only when the task is cancelled. everything seems fine to me here.
Then i need to start a task (not infinite) on a button click:
private void ExecuteRepairCommand(object o) {
Task.Run(() => {
...
});
}
it take ~30/40 seconds to start this new task.
if i use thread everything works correctly, the thread starts instantly. why? what's the cause?

By default tasks are scheduled to ThreadPool. ThreadPool won't create new threads when you schedule lot of tasks. It will wait for sometime before creating new threads(based on some heuristics). That's why you notice a delay in starting of your tasks. I've explained it earlier here.
Back to your question. If your task is long running, you should really consider using LongRunning flag. It will instruct the Task Scheduler to give it a new thread; so your task can run independently for a long time without affecting other tasks.
Task.Factory.StartNew(() =>
{
...
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);

Related

Task.Run() Delay, when first called

I am running a Thread which get called in a static interval.
In this Thread I am running several Tasks (20-200).
All this works fine, but when the Thread gets called the first time, it takes like ~1 sec for one Tasks to start.
As soon as the while loop is in the second loop or when the Thread stops, and gets called a second time, the problem is gone.
public static async void UpdateThread()
{
while(!stop)
{
foreach (DSDevice device in DSDevices)
{
var task = Task.Run(() =>
{
// Delay is measured here
// Do Stuff
});
}
//No Delay
await Task.WhenAll(tasks);
Thread.Sleep(Sleeptime);
}
}
The Task.Run runs the code on the ThreadPool, and the ThreadPool creates initially a limited number of threads on demand. You can increase this limit with the SetMinThreads method:
ThreadPool.SetMinThreads(200, 200);
...but check out the documentation before doing so. Increasing this threshold is not something that you should do without thinking. Having too many ThreadPool threads defeats the purpose of having a pool in the first place. Think whether it's better to have a dedicated thread per device, for the whole life-time of the program.
As a side note, if I was in your shoes I would not parallelize the processing of the devices by creating tasks manually. I would use the Parallel.ForEach method, which exists for exactly this kind of job. As a bonus it allows to control the degree of parallelism, either to a specific number or to -1 for unlimited parallelism:
public static async Task MonitorDevicesPeriodicAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Task delayTask = Task.Delay(MonitorDevicesPeriodMilliseconds);
await Task.Run(() =>
{
ParallelOptions options = new() { MaxDegreeOfParallelism = -1 };
Parallel.ForEach(DSDevices, options, device =>
{
// Do Stuff with device
});
});
await delayTask;
}
}
The Parallel.ForEach invokes also the delegate on the ThreadPool (by default), and it can saturate it as easily as the await Task.WhenAll(tasks) approach, so you might need to use the ThreadPool.SetMinThreads method as well.
Three more off topic suggestions: prefer async Task over async void. Async void is intended for event handler only. Also use a CancellationToken for stopping the while loop instead of a non-volatile bool stop field. In a multithreaded environment, it's not guaranteed that the mutation of the field from one thread will be visible from other threads. Alternatively declare the field as volatile. Finally use the Task.Delay instead of the Thread.Sleep, create the Task.Delay task at the start of the iteration and await it at the end, for a stable periodic invocation.

Cancel a Task if it hasnt completed in 5 seconds [duplicate]

I am using an external library that has async methods, but not CancellationToken overloads.
Now currently I am using an extension method from another StackOverflow question to add a CancellationToken:
public async static Task HandleCancellation(this Task asyncTask, CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested. http://stackoverflow.com/a/18672893/1149773
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(() =>
tcs.TrySetCanceled(), useSynchronizationContext: false);
Task cancellationTask = tcs.Task;
// Create a task that completes when either the async operation completes, or
// cancellation is requested.
Task readyTask = await Task.WhenAny(asyncTask, cancellationTask);
// In case of cancellation, register a continuation to observe any unhandled exceptions
// from the asynchronous operation (once it completes). In .NET 4.0, unobserved task
// exceptions would terminate the process.
if (readyTask == cancellationTask)
asyncTask.ContinueWith(_ => asyncTask.Exception,
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
await readyTask;
}
However the underlying task still executes to completion. This wouldn't be much of a problem, but sometimes the underlying task never completes and consumes 99% of my CPU.
Is there any way to "kill" the task without killing the process?
I am using an extension method from another StackOverflow question
That code is very old.
The modern AsyncEx approach is an extension method Task.WaitAsync, which looks like this:
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token;
await myTask.WaitAsync(ct);
I like how the API ended up because it's more clear that it's the wait that is cancelled, not the operation itself.
Is there any way to "kill" the task without killing the process?
No.
The ideal solution is to contact the authors of the library you're using and have them add support for CancellationToken.
Other than that, you're in the "cancel an uncancelable operation" scenario, which can be solved by:
Putting the code in a separate process, and terminating that process on cancellation. This is the only fully safe but most difficult solution.
Putting the code in a separate app domain, and unloading that app domain on cancellation. This is not fully safe; terminated app domains can cause process-level resource leaks.
Putting the code in a separate thread, and terminating that thread on cancellation. This is even less safe; terminated threads can corrupt program memory.
As you suggest you can cancel a task by passing in a CancellationToken and then calling Cancel.
As for how you'd go about triggering that cancellation depends on the nature of your application.
A few possible scenarios
Carry on until you click cancel
Cancel after a fixed time
Cancel if there's been no progress for a fixed time
In case 1 you simply cancel the task from your cancel button, for example
private void cancel_Click(object sender, RoutedEventArgs e)
{
...
cts = new CancellationTokenSource();
await MyAsyncTask(cts.Token);
cts.Cancel();
...
}
In case 2 you could start a timer when you start your task and then cancel the task after a set time using CancelAfter, for example
private void start_Click(object sender, RoutedEventArgs e)
{
...
cts = new CancellationTokenSource();
cts.CancelAfter(30000);
await MyAsyncTask(cts.Token);
...
}
In case 3 you could do something with progress, for example
private void start_Click(object sender, RoutedEventArgs e)
{
...
Progress<int> progressIndicator = new Progress<int>(ReportProgress);
cts = new CancellationTokenSource();
await MyAsyncTask(progressIndicator, cts.Token);
...
}
void ReportProgress(int value)
{
// Cancel if no progress
}
Here are a few useful links
Parallel programming, task cancellation, progress and cancellation, cancel tasks after set time, and cancel a list of tasks.
The only way I can think of is to change the TaskScheduler and mange the creation of the threads that are used for the tasks yourself. That is a lot of work.
The basic concept is to create your own implementation of the TaskScheduler, start a new task with your own scheduler assigned. This way you get your scheduler to be the current one and start your problematic task from this task.
There are still reason that may not work. If the task causing you trouble creates more tasks using the default task scheduler you still got the same problem. (Task.Run does so)
How ever if they are using the async/await key words your scheduler will remain active.
Now with the scheduler under your own control, you can kill any task by using Thread.Abort.
To get a idea about the implementation afford, you should have a look at the ThreadPoolTaskScheduler. That is the default implementation of the scheduler.
As I said this is a lot of work, but the only way I can think of to kill task that can't be cancelled.
To get a test running if that even works at all you may only want to implement the behaviour the ThreadPoolTaskScheduler has for the TaskCreationOptions.LongRunning option. So spawning a new thread for each task.

How to pass LongRunning flag specifically to Task.Run()?

I need a way to set an async task as long running without using Task.Factory.StartNew(...) and instead using Task.Run(...) or something similar.
Context:
I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread). This can be achieved through the code below:
var cts = new CancellationTokenSource();
Task t = Task.Factory.StartNew(
async () => {
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
} }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
The problem is that Task.Factory.StartNew(...) does not return the active async task that is passed in but rather a 'task of running the Action' which functionally always has taskStatus of 'RanToCompletion'. Since my code needs to be able to track the task's status to see when it becomes 'Canceled' (or 'Faulted') I need to use something like below:
var cts = new CancellationTokenSource();
Task t = Task.Run(
async () => {
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
} }, cts.Token);
Task.Run(...), as desired, returns the async process itself allowing me to obtain actual statuses of 'Canceled' or 'Faulted'. I cannot specify the task as long running, however. So, anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?
I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread)... anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?
There's a few problems with this. First, "long running" does not necessarily mean a dedicated thread - it just means that you're giving the TPL a hint that the task is long-running. In the current (4.5) implementation, you will get a dedicated thread; but that's not guaranteed and could change in the future.
So, if you need a dedicated thread, you'll have to just create one.
The other problem is the notion of an "asynchronous task". What actually happens with async code running on the thread pool is that the thread is returned to the thread pool while the asynchronous operation (i.e., Task.Delay) is in progress. Then, when the async op completes, a thread is taken from the thread pool to resume the async method. In the general case, this is more efficient than reserving a thread specifically to complete that task.
So, with async tasks running on the thread pool, dedicated threads don't really make sense.
Regarding solutions:
If you do need a dedicated thread to run your async code, I'd recommend using the AsyncContextThread from my AsyncEx library:
using (var thread = new AsyncContextThread())
{
Task t = thread.TaskFactory.Run(async () =>
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
}
});
}
However, you almost certainly don't need a dedicated thread. If your code can execute on the thread pool, then it probably should; and a dedicated thread doesn't make sense for async methods running on the thread pool. More specifically, the long-running flag doesn't make sense for async methods running on the thread pool.
Put another way, with an async lambda, what the thread pool actually executes (and sees as tasks) are just the parts of the lambda in-between the await statements. Since those parts aren't long-running, the long-running flag is not required. And your solution becomes:
Task t = Task.Run(async () =>
{
while (true)
{
cts.Token.ThrowIfCancellationRequested(); // not long-running
try
{
"Running...".Dump(); // not long-running
await Task.Delay(500, cts.Token); // not executed by the thread pool
}
catch (TaskCanceledException ex) { }
}
});
Call Unwrap on the task returned from Task.Factory.StartNew this will return the inner task, which has the correct status.
var cts = new CancellationTokenSource();
Task t = Task.Factory.StartNew(
async () => {
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
} }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap();
On a dedicated thread, there's nothing to yield to. Don't use async and await, use synchronous calls.
This question gives two ways to do a cancellable sleep without await:
Task.Delay(500, cts.Token).Wait(); // requires .NET 4.5
cts.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(500)); // valid in .NET 4.0 and later
If part of your work does use parallelism, you can start parallel tasks, saving those into an array, and use Task.WaitAny on the Task[]. Still no use for await in the main thread procedure.
This is unnecessary and Task.Run will suffice as the Task Scheduler will set any task to LongRunning if it runs for more than 0.5 seconds.
See here why.
https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
You need to specify custom TaskCreationOptions. Let’s consider each of
the options. AttachedToParent shouldn’t be used in async tasks, so
that’s out. DenyChildAttach should always be used with async tasks
(hint: if you didn’t already know that, then StartNew isn’t the tool
you need). DenyChildAttach is passed by Task.Run. HideScheduler might
be useful in some really obscure scheduling scenarios but in general
should be avoided for async tasks. That only leaves LongRunning and
PreferFairness, which are both optimization hints that should only be
specified after application profiling. I often see LongRunning misused
in particular. In the vast majority of situations, the threadpool will
adjust to any long-running task in 0.5 seconds - without the
LongRunning flag. Most likely, you don’t really need it.
The real issue you have here is that your operation is not in fact long running. The actual work you're doing is an asynchronous operation, meaning it will return to the caller basically immediately. So not only do you not need to use the long running hint when having the task scheduler schedule it, there's no need to even use a thread pool thread to do this work, because it'll be basically instantaneous. You shouldn't be using StartNew or Run at all, let alone with the long running flag.
So rather than taking your asynchronous method and starting it in another thread, you can just start it right on the current thread by calling the asynchronous method. Offloading the starting of an already asynchronous operation is just creating more work that'll make things slower.
So your code simplifies all the way down to:
var cts = new CancellationTokenSource();
Task t = DoWork();
async Task DoWork()
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException) { }
}
}
I think the consideration should be not how long the thread run but how much of its time is it really working.
In your example there is short work and them await Task.Delay(...).
If this is really the case in your project you probably shouldn't use a dedicated thread for this task and let it run on the regular thread pool. Every time you'll call await on an IO operation or on Task.Delay() you'll release the thread for other tasks to use.
You should only use LongRunning when you'll decrease your thread from the thread-pool and never give it back or give it back only for a small percentage of the time. In such a case (where the work is long and Task.Delay(...) is short in comparison) using a dedicated thread for the job is a reasonable solution.
On the other hand if your thread is really working most of the time it will consume system resources (CPU time) and maybe it doesn't really matter if it holds a thread of the thread-pool since it is preventing other work from happening anyway.
Conclusion? Just use Task.Run() (without LongRunning) and use await in your long running task when and if it is possible. Revert to LongRunning only when you actually see the other approach is causing you problems and even then check your code and design to make sure it is really necessary and there isn't something else you can change in your code.

Task stays in WaitingToRun state for abnormally long time

I've got a program that handles a variety of tasks running in parallel. A single task acts as a manager of sorts, making sure certain conditions are met before the next task is ran. However, I've found that sometimes a task will sit in the WaitingToRun state for a very long time. Here's the following code:
mIsDisposed = false;
mTasks = new BlockingCollection<TaskWrapper>(new ConcurrentQueue<TaskWrapper>());
Task.Factory.StartNew(() => {
while (!mIsDisposed) {
var tTask = mTasks.Take();
tTask.task.Start();
while (tTask.task.Status == TaskStatus.WaitingToRun) {
Console.WriteLine("Waiting to run... {0}", tTask.task.Id);
Thread.Sleep(200);
}
tTask.ready.Wait();
}
mTasks.Dispose();
});
DoWork();
DoWork();
DoWork();
DoWork();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWork();
TaskWrapper is very simply defined as:
private class TaskWrapper
{
public Task task { get; set; }
public Task ready { get; set; }
}
And tasks are only currently added in 2 places:
public void DoWork()
{
DoWorkAsync().Wait();
}
public Task DoWorkAsync()
{
ManualResetEvent next = new ManualResetEvent(false);
Task task = new Task(() => ActualWork(next));
Task ready = Task.Factory.StartNew(() => next.Wait());
mTasks.Add(new TaskWrapper() {
task = task,
ready = ready
});
return task;
}
Where ActualWork(next) calls next.Set().
This queues work and waits until next has been set before allowing the next work item to proceed. You can either wait for the entire task to finish before continuing by calling DoWork() or queue multiple tasks at once (which are supposed to run after next has been set).
However, when adding a task via DoWorkAsync(), after calling tTask.task.Start(), tTask.task sits in the WaitingToRun state for a loooong time (like 30 seconds to a minute), then magically starts running. I've monitored this using the while loop, and Waiting To Run... # will display for quite some time.
Calling DoWork() always runs immediately. I'm sure this has something to do with calling Wait on the task that is set to run.
I'm at a loss, here.
UPDATE:
I've managed to make the code work, but I'd still like to know why there's an issue in the first place.
After some experimental changes, I've managed to fix my own problem, but it's more of a "Oh, so I just can't do that" rather than a good fix. It turns out my problem was enqueuing tasks to run too quickly. By modifying DoWorkAsync() to no longer use Task.Factory.StartNew and changing tTask.ready.Wait() to tTask.ready.RunSynchronously I've managed to solve my issue.
Is there a reason the TaskScheduler is delaying the scheduling of my tasks? Am I saturating some underlying resources? What's going on here?
The threads will be run in the system's thread pool. The thread pool has a minimum number of threads available at all times (see ThreadPool.SetMinThreads()). If you try to create more than that many threads, a delay of approximately 500ms will be introduced between each new thread starting.
There is also a maximum number of threads in the thread pools (see ThreadPool.GetMaxThreads()), and if you reach that limit no new threads will be created; it will wait until an old thread dies before scheduling a new one (or rather, rescheduling the old one to run your new thread, of course).
You are unlikely to be hitting that limit though - it's probably over 1000.
Ok, I've just been faced with a similar issue. A bit of code that created and started a task ran, but the task never started (it just changed status to WaitingToRun)
Having tried the other options in this thread to no avail I thought about it a bit more, and realised that the code that was calling this method was itself called in a continuation task, that had been specified to run on the UI task scheduler (As it needed to update the UI)...
So something like
void Main()
{
var t1 = new Task(() => Console.WriteLine("hello, I'm task t1"));
t1.ContinueWith(t => CreateAndRunASubTask(), TaskScheduler.FromCurrentSynchronizationContext());
t1.Start();
Console.WriteLine("All tasks done with");
}
// Define other methods and classes here
public void CreateAndRunASubTask()
{
var tsk = new Task(() => Console.WriteLine("hello, I'm the sub-task"));
tsk.Start();
Console.WriteLine("sub-task has been told to start");
tsk.Wait();
// the code blocks on tsk.Wait() indefinately, the tsk status being "WaitingToRun"
Console.WriteLine("sub-task has finished");
}
The fix turned out to be pretty simple - when specifying the continuation task you need to specify the TaskContinuationOption: TaskContinuationOptions.HideScheduler
This has the effect of... (taken from the XML comment)
Specifies that tasks created by the continuation by calling methods
such as System.Threading.Tasks.Task.Run(System.Action) or
System.Threading.Tasks.Task.ContinueWith(System.Action{System.Threading.Tasks.Task})
see the default scheduler (System.Threading.Tasks.TaskScheduler.Default) rather
than the scheduler on which this continuation is running as the current scheduler.
ie (in my example)
t1.ContinueWith(t =>
CreateAndRunASubTask(),
System.Threading.CancellationToken.None,
TaskContinuationOptions.HideScheduler,
TaskScheduler.FromCurrentSynchronizationContext());
Hope this helps someone, as it stumped me for a good while!
Just faced similar issue.
I have a bunch of similar tasks running inifite loops, one of that tasks from time to time stays in WaitingToRun state permamently.
Creating tasks in that way did the trick for me:
_task = new Task(() => DoSmth(_cancellationTokenSource.Token), TaskCreationOptions.LongRunning);
_task.Start();

Sleep task (System.Threading.Tasks)

I need to create thread which will replace photo in Windows Forms window, than waits for ~1second and restore the previous photo.
I thought that the following code:
TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(() =>
{
pic.Image = Properties.Resources.NEXT;
Thread.Sleep(1000);
pic.Image = Properties.Resources.PREV;
}, CancellationToken.None, TaskCreationOptions.LongRunning, ui)
do the job, but unfortunately doesn't. It freezes main UI thread.
That's because it's not guaranteed that there is one thread per one task. One thread can be used for processing several tasks.
Even TaskCreationOptions.LongRunning option can't help.
How I can fix it?
Thread.Sleep is a synchronous delay. If you want an asynchronous delay then use Task.Delay.
In C# 5, which is at present in beta release, you can simply say
await Task.Delay(whatever);
in an asynchronous method, and the method will automatically pick up where it left off.
If you are not using C# 5 then you can "manually" set whatever code you want to be the continuation of the delay yourself.
When you pass a new TaskScheduler that is from the current synchronization context, you actually telling the task to run on the UI thread. You actually want to do that, so you can update the UI component, however you don't want to sleep on that thread, since it will block.
This is a good example of when .ContinueWith is ideal:
TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(() =>
{
pic.Image = Properties.Resources.NEXT;
},
CancellationToken.None,
TaskCreationOptions.None,
ui);
task.ContinueWith(t => Thread.Sleep(1000), TaskScheduler.Default)
.ContinueWith(t =>
{
pic.Image = Properties.Resources.Prev;
}, ui);
EDIT (Removed some stuff and added this):
What happens is that we're blocking the UI thread for only enough time to update pic.Image. By specifying the TaskScheduler, you're telling it what thread to run the task on. It's important to know that the relationship between Tasks and Threads is not 1:1. In fact, you can have 1000 tasks running on relatively few threads, 10 or less even, it all depends on the amount of work each task has. Do not assume each task you create will run on a separate thread. The CLR does a great job of balancing performance automatically for you.
Now, you don't have to use the default TaskScheduler, as you've seen. When you pass the UI TaskScheduler, that is TaskScheduler.FromCurrentSynchronizationContext(), it uses the UI thread instead of the thread pool, as TaskScheduler.Default does.
Keeping this in mind, let's review the code again:
var task = Task.Factory.StartNew(() =>
{
pic.Image = Properties.Resources.NEXT;
},
CancellationToken.None,
TaskCreationOptions.None,
ui);
Here, we're creating and starting a task that will run on the UI thread, that will update the Image property of pic with your resource. While it does this, the UI will be unresponsive. Fortunately, this is a likely a very fast operation, and the user won't even notice.
task.ContinueWith(t => Thread.Sleep(1000), TaskScheduler.Default)
.ContinueWith(t =>
{
pic.Image = Properties.Resources.Prev;
}, ui);
With this code, we're calling the ContinueWith method. It does exactly what it sounds like. It returns a new Task object that will execute the lambda parameter when it runs. It will be started when the task has either completed, faulted or been cancelled. You can control when it will run by passing in TaskContinuationOptions. However, we're also passing a different task scheduler as we did before. This is the default task scheduler that will execute a task on a thread pool thread, thus, NOT blocking the UI. This task could run for hours and your UI will stay responsive (don't let it), because it's a separate thread from the UI thread that you are interacting with.
We've also called ContinueWith on the tasks we've set to run on the default task scheduler. This is the task that will update the image on the UI thread again, since we've passed that same UI task scheduler to the executing task. Once the threadpool task has finished, it will call this one on the UI thread, blocking it for a very short period of time while the image is updated.
You should be using a Timer to perform a UI task at some point in the future. Just set it to run once, and with a 1 second interval. Put the UI code in the tick event and then set it off.
If you really wanted to use tasks, you'd want to have the other task not run in the UI thread but rather in a background threat (i.e. just a regular StartNew task) and then use the Control.Invoke inside of the task to run a command on the UI thread. The problem here is that is' band-aid-ing the underlying problem of starting a task just to have it sleep. Better to just have the code not even execute in the first place for the full second.

Categories

Resources