Task.Factory.StartNew vs Task.Factory.FromAsync

31,256

Solution 1

var task = Task.Factory.StartNew(() => { IOMethod(); });
task.Wait();

This will block a thread pool thread while IOMethod() is executing and also block your current thread because of the Wait(). Total blocked threads: 2.


var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.Wait();

This will (most likely) perform the operation asynchronously without using a thread, but it will block the current thread because of the Wait(). Total blocked threads: 1.


IOMethod();

This will block the current thread while IOMethod() is executing. Total blocked threads: 1.

If you need to block the current thread, or if blocking it is okay for you, then you should use this, because trying to use TPL won't actually give you anything.


var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
await task;

This will perform the operation asynchronously without using a thread, and it will also wait for the operation to complete asynchronously, thanks to await. Total blocked threads: 0.

This is what you should use if you want to take advantage of asynchrony and you can use C# 5.0.


var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.ContinueWith(() => /* rest of the method here */);

This will perform the operation asynchronously without using a thread, and it will also wait for the operation to complete asynchronously, thanks to ContinueWith(). Total blocked threads: 0.

This is what you should use if you want to take advantage of asynchrony and you can't use C# 5.0.

Solution 2

(1) will (likely) cause the .NET thread pool to process your Task.

(2) will use whatever mechanism your BeginIOMethod / EndIOMethod pair natively uses to handle the asynchronous part, which may or may not involve the .NET thread pool.

For example, if your BeginIOMethod is sending a TCP message across the internet, and at a later time the recipient is going to send you a TCP message in response (received by EndIOMethod), then the asynchronous nature of the operation is not being provided by the .NET thread pool. The TCP library being used is providing the asynchronous part.

This can be accomplished by using the TaskCompletionSource class. Task.Factory.FromAsync can create a TaskCompletionSource<T>, return its Task<T>, then use EndIOMethod as a trigger to place the Result into the Task<T> that was returned form Task.Factory.FromAsync at the time of calling.

What's the performance difference in terms of the resource utilization?

The difference between (1) and (2) is primarily just whether the .NET thread pool is going to have its workload added to or not. In general, the correct thing to do is to choose Task.Factory.FromAsync if you only have a Begin... / End... pair and Task.Factory.StartNew otherwise.


If you're using C# 5.0, then you should be using the non-blocking await task; instead of task.Wait();. (See svick's answer.)

Share:
31,256

Related videos on Youtube

soleiljy
Author by

soleiljy

Updated on July 09, 2022

Comments

  • soleiljy
    soleiljy almost 2 years

    Let's suppose we have a I/O bound method (such as a method making DB calls). This method can be run both in synchronously and asynchronously. That is,

    1. Sync:

      IOMethod()
      
    2. Async:

      BeginIOMethod()
      EndIOMethod()
      

    Then when we execute the method in different ways as shown below, what's the performance difference in terms of the resource utilization?

    1. var task = Task.Factory.StartNew(() => { IOMethod(); });
      task.Wait();
      
    2. var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
      task.Wait();
      
    • Servy
      Servy almost 11 years
      The short answer, you're (probably) not having a thread pool thread sit there doing nothing when using FromAsync, you are if you use StartNew. If you're doing a lot of stuff, stressing the thread pool could be a performance problem.
    • anton.burger
      anton.burger almost 11 years
  • Tombala
    Tombala almost 11 years
    Isn't (2) still going to have to launch a thread to execute the BeginIOMethod? Wouldn't that still be a thread in the thread pool? According to this question, FromAsync also uses thread pool.
  • Timothy Shields
    Timothy Shields almost 11 years
    @Tombala The BeginIOMethod is run completely synchronously on the thread calling FromAsync. --- On the answer to that question, see this comment: "The resulting Task will, by default, be executed on a thread pool thread" - only if the operation actually requires a thread to run. For example, I/O operations will use an I/O completion port rather than a thread. This scales better as threads are a limited and somewhat expensive resource.
  • Tombala
    Tombala almost 11 years
    Correct but the rest of the conversation also points to the possibility of those callbacks occurring on a thread pool thread. So, depending on the implementation of how Begin and End is done, the utilization could be same, less, or even more. E.g. if Begin and End launch threads to do their async operation instead of port callbacks, then it is possible that they might add to the thread pool, too.
  • svick
    svick almost 11 years
    “the correct thing to do is to choose Task.Factory.FromAsync [or] Task.Factory.StartNew” No, the correct thing here is not to use TPL at all, or use FromAsync() and non-blocking wait.
  • Tombala
    Tombala almost 11 years
    I think the "task.Wait()" was introduced just for example's sake. I'm sure when the task is executing, the OP is intending to do something extra along the way. So I don't think you should count the calling thread as being a blocked thread. But if he was serious about just waiting, then you are correct.
  • svick
    svick almost 11 years
    @Tombala I don't see anything in the question that would indicate that. But it's certainly a possibility.
  • Timothy Shields
    Timothy Shields almost 11 years
    @svick "The correct thing here is to not use TPL" - the question is tagged with TPL, so to answer the question as asked I will be using TPL. I'm missing your point? Maybe the OP isn't using C# 5?
  • Timothy Shields
    Timothy Shields almost 11 years
    @Tombala From my answer, with emphasis added: "(2) will use whatever mechanism your BeginIOMethod / EndIOMethod pair natively uses to handle the asynchronous part, which may or may not involve the .NET thread pool." So why are you acting like you're saying something new when you say "it is possible that they might add to the thread pool, too"?
  • svick
    svick almost 11 years
    @TimothyShields If the goal is to immediately synchronously wait until the operation completes, then not using TPL is the best choice. TPL will just add overhead with no benefit. Just because the question is tagged TPL doesn't mean the right answer has to be TPL.
  • Timothy Shields
    Timothy Shields almost 11 years
    @svick I believe the OP was writing the task.Wait() line immediately after not intending it to mean that the next line is actually a wait. Very likely the OP is doing other work in between the start of the task and the wait. If you're pre-C# 5.0, this is idiomatic usage of TPL (assuming other stuff going on between task start and wait).