9

What are the differences between this two methods that seems to do the same thing? Can it be done even with async/await?

public Task<int> TaskMaxAsync1 ( Task<int>[] my_ints )
{
    return Task.WhenAll( my_ints )
    .ContinueWith ( x => x.Result.Where ( i => i%2 != 0 ).Max( ) ) ;
}

public Task<int> TaskMaxAsync2 ( Task<int>[] my_ints )
{
    var numbers = Task.WhenAll( my_ints ).Result ;
    return Task.FromResult( numbers.Where( i => i%2 != 0 ).Max( ) ) ;
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
PCH
  • 101
  • 4
  • 1
    One difference would be that the one using `.Result` is prone to deadlocking - http://stackoverflow.com/questions/17248680/await-works-but-calling-task-result-hangs-deadlocks – Lasse V. Karlsen Aug 24 '15 at 08:59
  • @LasseV.Karlsen They both use .Result – TheLethalCoder Aug 24 '15 at 09:01
  • 5
    Agh, yes, ok, let me reword. The one using `.Result` outside of `ContinueWith` (ie. the second one) is prone to deadlocking. The only place it is safe to read `.Result` is from a task that is guaranteed to have completed. Reading `.Result` from a task that has not yet completed may deadlock. – Lasse V. Karlsen Aug 24 '15 at 09:01

3 Answers3

8

Differences between Result and ContinueWith

Result will synchronously block until the task completes, and will wrap exceptions in an AggregateException.

ContinueWith will register a callback with the task, and invoke that callback when the task completes.

For asynchronous code, both Result and ContinueWith should be replaced by await.

Can it be done even with async/await?

Sure, like this:

public async Task<int> MaxAsync(Task<int>[] my_ints)
{
  int[] ints = await Task.WhenAll(my_ints);
  return ints.Where(i => i % 2 != 0).Max();
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
7

What are the differences between this two methods that seems to do the same thing?

The difference is that the former returns a hot task to the caller, while the latter synchronously blocks, then re-wraps the result in a Task using Task.FromResult. The latter is also a common case for deadlocks, if you're running inside an environment that has a custom SynchronizationContext.

Can it be done even with async/await?

Yes, it can:

public async Task<int> MaxAsync(Task<int>[] myInts)
{
    int[] results = await Task.WhenAll(myInts);
    return results.Max(i => i % 2 != 0 ? i : (int?)null) ?? 0;
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
3

task0.Result will synchronously block and wait for task0 to complete, while task1.ContinueWith will not wait for task1 to complete but returns a new Task(which will run when task1 completes) immediately.

So, the two methods in your example do not behave the same. In your second method, if my_ints were not all ran to complete when they were passed to the WhenAll method, the .Result will synchronously block the calling thread for whatever long time that all tasks within my_ints would take to finish, and if some of them throws exception, TaskMaxAsync2 throws.

However the first method will return immediately even my_ints would never finish or throw exception.

Paul Chen
  • 1,873
  • 1
  • 16
  • 28