Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 764
Ryan Avatar asked Nov 14 '14 00:11

Ryan


People also ask

Does Task run Use thread pool?

The main purpose of Task. Run() is to execute CPU-bound code in an asynchronous way. It does this by pulling a thread from the thread pool to run the method and returning a Task to represent the completion of the method.

What does Task run do C#?

The Run method allows you to create and execute a task in a single method call and is a simpler alternative to the StartNew method. It creates a task with the following default values: Its cancellation token is CancellationToken.

Does Task run block thread?

Run is misused to run IO blocking tasks. Although the code will work just fine (e.g UI not not freeze) but it is still a wrong approach. This is because Task. Run will still block a thread from thread pool the entire time until it finishes the method.

What is Task factory StartNew in C#?

StartNew(Action<Object>, Object, CancellationToken, TaskCreationOptions, TaskScheduler) Creates and starts a task for the specified action delegate, state, cancellation token, creation options and task scheduler.


1 Answers

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) { }   } }); 
like image 173
Stephen Cleary Avatar answered Sep 20 '22 00:09

Stephen Cleary