2

Consider the following call:

Getstuff(await getFoo, await getBar, await getQuux); 

Inside Getstuff:

var fooLookup = getFoo?.Entries.ToLookup(x => x.Something); var barDictionary = getBar?.Entries.ToDictionary(x => x.Something); var quuxDictionary = getQuux.Entries.ToDictionary(x => x.Something); 

I have the impression this misses a concurrency opportunity that could be given by:

Getstuff(getFoo, getBar, getQuux); 

and Getstuff:

Task<getFooLookup> fooLookup1; Task<getBarDictionary> barDictionary1; Task<getQuuxDictionary> quuxDictionary1; Task.WaitAll(fooLookup1 = getFoo, barDictionary1 = getBar, quuxDictionary1 = getQuux); var fooLookup = fooLookup1.Result.Entries.ToLookup(x => x.Something); var barDictionary = barLookup1.Result?.Entries.ToDictionary(x => x.Something); var quuxDictionary = quuxLookup1.Result?.Entries.ToDictionary(x => x.Something); 

Would the second option be parallelized (if it works), and if so, what risks would I be incurring for example a database lookup were involved? Any simplification I might be missing?

EDIT

The three parameters are assigned like so:

async Task<foo> GetFoo(ICollection<Guid> stuffList) { .. some async code.. } ... var getFoo = GetFoo(list); 
21
  • 1
    your code example is way too incomplete to comment on its behavior. Commented Mar 17, 2017 at 20:17
  • If getFoo/getBar/getQuux are tasks, then the code is already running concurrently. Commented Mar 17, 2017 at 20:21
  • @StephenCleary even in the first case where we have three awaits? Commented Mar 17, 2017 at 20:21
  • 1
    @OtávioDécio Indeed, we know that the expression is awaitable, and thus resolves to a Task, but we don't know when the task it resolves to is created, or when it actually starts performing whatever operation the Task represents. That will depend on the specifics of the code. Commented Mar 17, 2017 at 20:25
  • 1
    @Evk Describing every single possible implementation of a program and the specific semantics of all of them is way too broad. There are lots of different ways of writing programs, and they're going to have wildly varying semantics. Commented Mar 17, 2017 at 20:44

2 Answers 2

1

If you want to achieve parallelism and you have 3 methods that return say Task<T1>, Task<T2>, Task<T3>, it's pretty simple: start all the tasks first, but don't await any, and only then await, either all of them together with await Task.WhenAll() (ideally; otherwise less preferably with the blocking Task.WaitAll()), or individually with await for each. So something like this:

Task<T1> getFooTask = GetFoo(); Task<T2> getBarTask = GetBar(); Task<T3> getQuuxTask = GetQuux(); // All tasks started (or at least queued) now, potentially (probably) running in parallel await Task.WhenAll(getFooTask, getBarTask, getQuuxTask); // All tasks complete now 

You can then inspect the results individually:

T1 fooResults = await getFooTask; T2 barResults = await getBarTask; T3 quuxResults = await getQuuxTask; 

or perhaps:

Getstuff(await getFooTask, await getBarTask, await getQuuxTask); 

You can also use .Result without blocking at this point, but using await again refactors better.

To await them individually (and still achieve parallelism), just drop the await Task.WhenAll() line.

As a side note, dropping the await Task.WhenAll() is a simple example showing the benefit of using await on a completed task instead of .Result. If you had used .Result instead, the behaviour would have changed from non-blocking to blocking.

Sign up to request clarification or add additional context in comments.

2 Comments

So, what's the benefit of using Task.WhenAll in the above example? Since I can do just fine without - why add it at all?
In this particular example, probably not a lot. Maybe save a couple of context switches, but that's relatively insignificant. It depends on context, though; if the requirement is to not process any individual result until all tasks are completed, it would be useful.
0

Your scenario is pretty vague. I am assuming here that all three of these awaitable tasks are already running concurrently, such that your original code is synchronously awaiting the individual results.

First, it's important to point out that your proposed change to the code does materially and detrimentally change the basic behavior of the code. Where the code used to potentially allow for asynchronous waiting of completion, your proposed change forces the thread to synchronously wait for completion. This should be avoided.

I'd thought that was reasonably obvious, but judging from the comments, there are folks who are confused about my intent here, hence this explicit caveat. With that in mind…

Your second example, ignoring your syntax errors, can (and should) be simplified to:

Task.WaitAll(getFoo, getBar, getQuux); var fooLookup = getFoo.Result.Entries.ToLookup(x => x.Something); var barDictionary = getBar.Result?.Entries.ToDictionary(x => x.Something); var quuxDictionary = getQuux.Result?.Entries.ToDictionary(x => x.Something); 

But in reality, if your method is not itself inherently an asynchronous operation, you should not force that onto it. It is simple enough for the caller to do that as needed:

Task.WaitAll(getFoo, getBar, getQuux); GetStuff(await getFoo, await getBar, await getQuux); 

The WaitAll() enforces the synchronous behavior your example has. Again, it's not that this is desirable. It's just that this is required, in order to be consistent with your example.

In this way, all three tasks are assured of having completed before you try to pass their results to GetStuff() and GetStuff() can remain its nice, simple synchronous self.

Using await just helps unpack exceptions, making them easier to handle, log, etc. You could of course use e.g. getFoo.Result instead of await getFoo.

But you don't even need that! Assuming these three tasks are all already running, then awaiting all three in sequence doesn't in any way prevent them from running concurrently. Even if the first is the last to finish, by the time that one finishes, the other two will have also and those awaits will complete immediately.

And importantly, doing it in this way prevents the thread from being blocked while you wait for the results to be available. This is much better than the synchronous waiting you'd proposed as an alternative.

In other words, your original code is fine, and better than what you'd proposed as an alternative. Don't change it.

18 Comments

@Servy: how about you explain in what way the above answer is incorrect. Or even not useful.
@Servy I appreciate Peter's civility regardless, which is sorely lacking in this site.
@PeterDuniho Well for starters, you're specifically stating in multiple places that you're making assumptions, and you don't have a basis for those assumptions, so your conclusions based on those unwarranted assumptions are therefore meaningless. Your proposed changes will also have wildly varying effects based on the specific of the code not shown. It won't ever have the effects you explain, under any of those situations, but the actual effects it will have have a number of possibilities. Your assessment of await is also completely incorrect, just in entirety.
@PeterDuniho 4) When a question doesn't provide sufficient information to be answerable the appropriate reaction is to work to fix the question such that it actually contains enough information to be answered, at which point a correct answer can actually be provided. Simply guessing at what you think is going on causes lots of problems, particularly when your guesses are wrong, making the answer not useful.
@PeterDuniho I'm not saying that the assumption is wrong, I'm saying that we have no idea whether or not it's right or wrong. You're providing an answer to a question without even knowing what the question is asking. Answers are expected to be demonstrably correct and you can't demonstrate that the answer is correct because the question doesn't contain enough information to do so. The fact that (that part) of the answer can't be proven wrong because the question is unanswerable doesn't mean it's automatically correct.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.