12

In other words, is

var task = SomeLongRunningOperationAsync();
task.Wait();

functionally identical to

SomeLongRunningOperation();

Stated another way, is

var task = SomeOtherLongRunningOperationAsync();
var result = task.Result;

functionally identical to

var result = SomeOtherLongRunningOperation();

According to Task.Wait and Inlining, if the Task being Wait’d on has already started execution, Wait has to block. However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.

Are those two cases merely a matter of deciding which thread the Task is going to run on, and if you're waiting on the result anyway, does it matter?

Is there any benefit to using the asynchronous form over the synchronous form, if nothing executes between the asynchronous call and the Wait()?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • Since SomeLongRunningOperation returns some result, I assume you mean `var result = task.Result` – EZI Jul 14 '15 at 23:34
  • Task.Result is a blocking op. No need to `Wait()` – EZI Jul 14 '15 at 23:35
  • True. So why `Wait()` at all? – Robert Harvey Jul 14 '15 at 23:36
  • You say it. No need for it. `var result = Task.Result;` is enough. – EZI Jul 14 '15 at 23:37
  • Read the article I linked. It tries to make a case that you're doing some useful work behind the scenes, but I'm having a hard time seeing that. – Robert Harvey Jul 14 '15 at 23:37
  • 1
    Robert, You `Wait` a task if it doesn't return anything. – EZI Jul 14 '15 at 23:38
  • I read it and I say it again. No need for `Wait`. `var result = Task.Result;` is enough in your example. – EZI Jul 14 '15 at 23:41
  • If the task is already started your current thread will only wait other thread to finish. I don't think it is important which thread executes your code unless you access some thread-static variables like HttpContext.Current OR WebOperatonContext.Current etc. – EZI Jul 14 '15 at 23:51

1 Answers1

10

Here are some differences:

  1. The computation might run on a different thread. It might run on the same thread if this task is CPU-based and can be inlined. This is non-deterministic.
  2. If no inlining happens one more thread will be in use during the computation. This usually costs 1MB of stack memory.
  3. Exceptions will be wrapped in AggregateException. The exception stack will be different.
  4. The task version might deadlock if the computation posts to the current synchronization context.
  5. If the thread-pool is maxed out this might deadlock if for the task to complete another task must be scheduled.
  6. Thread-local state, such as HttpContext.Current (which is not actually thread-local but almost), might be different.
  7. A thread abort of the main thread will not reach the task body (except in case of inlining). I'm not sure whether the wait itself will be aborted or not.
  8. Creating a Task induces a memory barrier which can have a synchronizing effect.

Does this matter? Decide for yourself by this list.

Are there benefits to doing this? I can't think of any. If your computation uses async IO the wait will negate the benefits that the async IO brings. The one exception would be fan-out IO, e.g. issuing 10 HTTP requests in parallel and waiting for them. That way you have 10 operations at the cost of one thread.

Note, that Wait and Result are equivalent in all these regards.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Sounds like the risks don't exceed the benefits (if there are any. You haven't mentioned any benefits). – Robert Harvey Jul 14 '15 at 23:55
  • 1
    What benefits do you expect? I don't know of any, except if you explicitly want any of the behaviors listed. – usr Jul 14 '15 at 23:56
  • I found this in some code that I didn't write, and assumed that there must be some reason it is this way. But there isn't anything around the code I found that suggests it needs any of these behaviors. – Robert Harvey Jul 14 '15 at 23:58
  • Yeah, could be the thread-local state or a custom scheduler using STA threads or some other esoteric reason. Normally, this should be commented with a reason because it is a smell. – usr Jul 14 '15 at 23:59
  • The only reason I can think of is that perhaps there's no synchronous version of this method available. But it seems to me that he could have written `result = SomeLongRunningOperationAsync().Result;` and saved himself a couple of lines of code. – Robert Harvey Jul 15 '15 at 00:02
  • 2
    Wait and Result are equivalent. If no sync version is available that's a good reason to do this assuming he really wants to have a synchronous mode of operation for some reason (maybe he's implementing an interface). In typical ASP.NET code this kind of blocking is very prone to deadlocks, though. – usr Jul 15 '15 at 10:12
  • 1
    @TheodorZoulias I edited the post. Indeed, there was a language mistake. Thanks for pointing it out. – usr Jan 28 '21 at 10:41