51

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();
    
svick
  • 236,525
  • 50
  • 385
  • 514
soleiljy
  • 1,121
  • 2
  • 13
  • 20
  • 2
    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. – Servy Jul 02 '13 at 17:56
  • Duplicate: http://stackoverflow.com/questions/5018897/tpl-taskfactory-fromasync-vs-tasks-with-blocking-methods – anton.burger Jul 02 '13 at 19:18

2 Answers2

84
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.

Lukas Körfer
  • 13,515
  • 7
  • 46
  • 62
svick
  • 236,525
  • 50
  • 385
  • 514
  • 4
    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. – Tombala Jul 02 '13 at 19:33
  • 2
    @Tombala I don't see anything in the question that would indicate that. But it's certainly a possibility. – svick Jul 02 '13 at 19:41
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.)

Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
  • 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](http://stackoverflow.com/questions/7784589/how-does-task-factory-fromasync-work-behave), FromAsync also uses thread pool. – Tombala Jul 02 '13 at 18:08
  • @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. – Timothy Shields Jul 02 '13 at 18:14
  • 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. – Tombala Jul 02 '13 at 19:19
  • “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. – svick Jul 02 '13 at 19:20
  • @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 Jul 02 '13 at 19:49
  • @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"? – Timothy Shields Jul 02 '13 at 19:50
  • @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. – svick Jul 02 '13 at 19:54
  • @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). – Timothy Shields Jul 02 '13 at 19:55