-2

Please look at this code:

class Program
{
    static async void DoStuff()
    {
        StreamWriter s = new StreamWriter("a.txt");
        WebClient wc = new WebClient();
        await wc.DownloadStringTaskAsync(new Uri("http://www.microsoft.com"));  //1
        //await s.WriteAsync ("123");                                           //2
        Thread.Sleep(10000);
        Console.WriteLine("DoStuffEnd");
    }
    static void Main(string[] args)
    {
        Program.DoStuff();
        Console.WriteLine("MainEnd");
        Console.Read();
    }

}

In this situation everything is ok, according to the logic of async/await.

Output:

  • MainEnd

  • After 10 seconds

  • DoStuffEnd

But if I comment (1) and uncomment (2), the output is:

  • After 10 seconds

  • DoStuffEnd

  • MainEnd

Why?

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
Marcin
  • 25
  • 2
  • Your `main()` cannot "end" before any other function because that would be "the end" of your program – Nasreddine May 10 '16 at 19:48
  • By the time the `await` generated code looks at the task to see if it has completed, which in your download case it hasn't, if by that time it has already completed (like it has for your stream write case), then it just continues synchronously. – Lasse V. Karlsen May 10 '16 at 19:53

2 Answers2

5

This code:

await x

if x is an awaitable (like a task) that has already completed, then the method just continues executing synchronously, like an ordinary method.

Stephen Cleary mentions this on his blog:

Async and Await:

The “await” keyword is where things can get asynchronous. Await is like a unary operator: it takes a single argument, an awaitable (an “awaitable” is an asynchronous operation). Await examines that awaitable to see if it has already completed; if the awaitable has already completed, then the method just continues running (synchronously, just like a regular method).

(my emphasis)

In the case of your download, it is unlikely that the download completes so fast that the task is marked as complete in the (relatively) few cpu cycles until the code reaches the await.

However, in the async write, there could even be an optimization that says that so and so small writes will just be completed synchronously because the overhead of async might dwarf the actual write, or you may perhaps experience that the write takes so little time that it completes before the code tries to await it.

A corollary is that you should use await Task.Delay(10000); instead of Thread.Sleep(10000); as the former will be more "async friendly".

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
1

What's happening is that StreamWriter already completes before the runtime can check to see if it's completed and return control to the main thread. In this case, the async method isn't blocking, it's the Thread.Sleep(10000) call. To fix this, try await Task.Delay(10000) instead. This should produce the asynchronous code that you are looking for.

Frank Bryce
  • 8,076
  • 4
  • 38
  • 56