Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the TPL run the Task on more than one thread?

Mono/Xamarin-specific answers are welcomed.

I'm running System.Threading.Tasks using Task.Run(). Will The TPL assign the created task to a single thread for the life of the task's execution? Or is it possible that the created task will get preempted while running, and then be scheduled again on a different thread?

Will Thread.CurrentThread.ManagedThreadId be constant over the life of a Task?

Is the answer different for long-running tasks?

Is there a way to control TPL behavior in this regard?

like image 489
jeff7091 Avatar asked Jul 25 '14 15:07

jeff7091


People also ask

Can Task have multiple threads?

Using the Task Parallel Library in . NET 4.0The concept of following more than one thread at a time introduces the subject of multi-tasking and multi-threading. An application has one or more processes in it. Think of a process as a program running on your computer. Now each process has one or more threads.

How are threads different from TPL?

Compared to the classic threading model in . NET, Task Parallel Library minimizes the complexity of using threads and provides an abstraction through a set of APIs that help developers focus more on the application program instead of focusing on how the threads will be provisioned.

What is TPL Task Parallel Library and how it differs from threads?

Threading. Tasks namespaces. The purpose of the TPL is to make developers more productive by simplifying the process of adding parallelism and concurrency to applications. The TPL scales the degree of concurrency dynamically to most efficiently use all the processors that are available.

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.


2 Answers

Will The TPL assign the created task to a single thread for the life of the task's execution?

The guideline is that synchronous portions of work run on a single thread. So, if you pass a synchronous delegate to Task.Run, it will all run on a single thread:

await Task.Run(() => Thread.Sleep(5000)); // same thread after sleep

However, if you have asynchronous code, every await is a location in the code where the method is suspended. When the method resumes, it will resume on a thread pool thread (which may be a different thread).

await Task.Run(async () => await Task.Delay(5000)); // thread may change

The long-running flag (which cannot be passed to Task.Run) does not affect this behavior because it only applies to the first synchronous portion.

The normal way to control this is by using a custom TaskScheduler or SynchronizationContext. However, before going down that road, you may want to consider an alternative approach. There should be a better solution than forcing the method back onto the same thread. For example, thread-affine synchronization primitives all have async-compatible equivalents, thread-local storage can be replaced by closures / class fields / logical call context, etc.

like image 127
Stephen Cleary Avatar answered Nov 03 '22 15:11

Stephen Cleary


Task.Run will schedule the delegate to be executed in the thread pool, and the thread pool will only ever execute the given delegate on a single thread. It won't move it around between threads.

That said, a Task conceptually is merely the representation of the completion of some asynchronous operation at some point in the future. It can represent anything that finishes eventually. If you want to create a task that represents the execution of one delegate on one thread, then the execution of another delegate on a different thread, then you absolutely can do that.

Many methods using the async keyword will in fact do that. Since, in an application that doesn't have a synchronization context set, the continuations wired up as a result of await calls may (they can sometimes be optimized out) each be scheduled separately in the thread pool. For example, the following code run in a console app generates a task that could potentially print out three entirely different numbers. (Of course, they aren't required to be different; the thread pool could just happen to schedule the continuations to run on the same thread.)

public static async Task Foo()
{
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(100);
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(100);
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
like image 29
Servy Avatar answered Nov 03 '22 16:11

Servy