2

Are there any differences between the following two code snippets?

1: Use await all the time

await Task.WhenAll(x, y);
var a = await x;
var b = await y;
(await x).f();
(await y).g();

2: Use .Result after the first await.

await Task.WhenAll(x, y);
var a = x.Result;
var b = y.Result;
x.Result.f();
y.Result.g();
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
ca9163d9
  • 27,283
  • 64
  • 210
  • 413
  • 1
    Were you intending to use `a` and `b` in those snippets? Otherwise they are completely redundant. – Enigmativity Nov 05 '22 at 06:23
  • Not very clear. In the first code you're awaiting multiple time the same task, i.e. you first wait `x` in the `WhenAll`, then again right below, and a third time when calling `f`. The second code awaits `x` only once. – Mario Vernari Nov 05 '22 at 06:24
  • 2
    @MarioVernari - Which doesn't change the semantics of the code. I think that's what the OP is looking for. – Enigmativity Nov 05 '22 at 06:25
  • @Enigmativity still I don't catch what he's actually asking. To me, the two codes behaves differently, from a generic perspective (what really x and y does?) – Mario Vernari Nov 05 '22 at 06:28
  • I've updated the question. @Enigmativity is right, I want to know if there is any semantic or performance difference. – ca9163d9 Nov 05 '22 at 06:29
  • To me, they COULD be different. As an example, consider x (and y) as "insert a new record in the table". The first code inserts three records, whereas the second only once. Of course, from the `f` call perspective, there's no difference. Please, correct me if I'm wrong. – Mario Vernari Nov 05 '22 at 06:33
  • @ca9163d9 - You complicate the question with the redundant `a` and `b`. You should remove them or use them. I vote use them. – Enigmativity Nov 05 '22 at 06:34
  • 3
    @MarioVernari - No, it doesn't. It only runs the task once. You can `await` multiple times, but the task only runs once. – Enigmativity Nov 05 '22 at 06:34
  • It's like `x = 2 + 3;`. You can use `2 + 3` everywhere in your code, but it makes more sense to use `x`. – Poul Bak Nov 05 '22 at 06:36
  • @Enigmativity, the lines which use `a` and `b` are omitted. They are used right after the lines shown in the question. – ca9163d9 Nov 05 '22 at 06:37
  • @ca9163d9 - You should post complete code in your question. You just make it confusing otherwise. Give each line a purpose. – Enigmativity Nov 05 '22 at 06:50
  • Catchy! I just made shameful my day! thank you everybody for pointing me this. – Mario Vernari Nov 05 '22 at 06:51
  • 1
    I'm assuming `x` and `y` are `Task` and not `ValueTask`, otherwise it would be a completely different thing in both cases (errors in both as you can only access the result at most, once). – Jeff Mercado Nov 05 '22 at 06:56
  • @JeffMercado [there is no](https://stackoverflow.com/questions/45689327/task-whenall-for-valuetask) `Task.WhenAll` for `ValueTask`s. The `x` and `y` are certainly `Task`s – Theodor Zoulias Nov 05 '22 at 07:14
  • 1
    @TheodorZoulias ah is that right? well I learned something new :) – Jeff Mercado Nov 05 '22 at 18:14

1 Answers1

4

There will not be any behavioral difference between the two as both tasks are guaranteed to be finished after the Task.WhenAll.

The compiler seems not to be aware of this and generates a slightly more complicated state machine in the first case (I verified this by looking at the generated IL, see here for a proof).

However, this will hardly make any noticable performance difference.

(Stylistically, the multiple await would be preferrable to me as seeing a .Result in async code makes all sort of alarm bells ring).

Update: As Stephen Cleary explained in his answer here, await x and x.Result will behave differently (different Exception type) if the task failed. But this does not apply here because any failing task would have already made await Task.WhenAll(...) throw an exception.

Klaus Gütter
  • 11,151
  • 6
  • 31
  • 36
  • *"[...] there will be a behavior difference (different Exception type) if the task failed."* -- I don't think so. The `await Task.WhenAll(x, y);` will expose the error of either task, right there. – Theodor Zoulias Nov 06 '22 at 10:09
  • 1
    @TheodorZoulias oh, yes, of course you are right. The exception will be thrown already there so any later possible behavior differences between await x and x.Result do not matter. – Klaus Gütter Nov 06 '22 at 11:32