views:

131

answers:

2

For some reason there is a pause after the program below starts. I believe that WebClient().DownloadStringTaskAsync() is the cause.

class Program
{
    static void Main(string[] args)
    {
        AsyncReturnTask();

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

    public static async void AsyncReturnTask()
    {
        var result = await DownloadAndReturnTaskStringAsync();
        Console.WriteLine(result);
    }

    private static async Task<string> DownloadAndReturnTaskStringAsync()
    {
        return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
    }
}

As far as I understand my program should start counting from 0 to 15 immediately. Am I doing something wrong?

I had the same problem with the original Netflix download sample (which you get with CTP) - after pressing the search button the UI first freezes - and after some time it is responsive while loadning the next movies. And I believe it didn't freeze in Anders Hejlsberg's presentation at PDC 2010.

One more thing. When instead of

return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));

I use my own method:

return await ReturnOrdinaryTask();

Which is:

public static Task<string> ReturnOrdinaryTask()
{
    var t = Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("------------- " + i.ToString());
            Thread.Sleep(100);
        }
        return "some text";
    });
    return t;
}

It works as it should. I mean it doesn't load anything, but it starts immediately and doesn't block the main thread, while doing its work.

Edit

OK, what I believe right now is: the WebClient.DownloadStringTaskAsync function is screwed up. It should work without the initial blocking period, like this:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        Task.Factory.StartNew(() =>
            {
                cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
                cli.DownloadStringAsync(new Uri("http://www.weather.gov"));
            });

        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }
+1  A: 

While your program does block for a while, it does resume execution in the for loop, before the result is returned from the remote server.

Remember that the new async API is still single-threaded. So WebClient().DownloadStringTaskAsync() still needs to run on your thread until the request has been prepared and sent to the server, before it can await and yield execution back to your program flow in Main().

I think the results you are seeing are due to the fact that it takes some time to create and send the request out from your machine. First when that has finished, the implementation of DownloadStringTaskAsync can wait for network IO and the remote server to complete, and can return execution to you.

On the other hand, your RunOrdinaryTask method just initializes a task and gives it a workload, and tells it to start. Then it returns immediately. That is why you don't see a delay when using RunOrdinaryTask.

Here are some links on the subject: Eric Lippert's blog (one of the language designers), as well as Jon Skeet's initial blog post about it. Eric has a series of 5 posts about continuation-passing style, which really is what async and await is really about. If you want to understand the new feature in detail, you might want to read Eric's posts about CPS and Async. Anyways, both links above does a good job on explaining a very important fact:

  • Asynchronous != parallel

In other words, async and await does not spin up new threads for you. They just lets you resume execution of your normal flow, when you are doing a blocking operation - times where your CPU would just sit and do nothing in a synchronous program, waiting for some external operation to complete.

Edit

Just to be clear about what is happening: DownloadStringTaskAsync sets up a continuation, then calls WebClient.DownloadStringAsync, on the same thread, and then yields execution back to your code. Therefore, the blocking time you are seeing before the loop starts counting, is the time it takes DownloadStringAsync to complete. Your program with async and await is very close to be the equivalent of the following program, which exhibits the same behaviour as your program: An initial block, then counting starts, and somewhere in the middle, the async op finishes and prints the content from the requested URL:

    static void Main(string[] args)
    {
        WebClient cli = new WebClient();
        cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
        cli.DownloadStringAsync(new Uri("http://www.weather.gov")); // Blocks until request has been prepared

        for (int i = 0; i < 15; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }
    }

Note: I am by no means an expert on this subject, so I might be wrong on some points. Feel free to correct my understanding of the subject, if you think this is wrong - I just looked at the PDC presentation and played with the CTP last night.

driis
yeah - i see it's working like you said - but i belive it's some kind of a bug - the idea is that async operations like DownloadStringTaskAsync() doesn't block the main/ui/calling thread - all things that take some time should be started on other thread and return immediately (other way - what is the point?). So the question is : am i doing something wrong or the DownloadStringTaskAsync() method is screwed up - i know it's ctp/prototype right now. But the strange thing is that it seemed to work fine(no initial blocking) on Hejlsberg's computer during the presentation.
agend
could anybody check this code on his own computer and confirm the initial blocking behavior ? TIA Arek
agend
@agend, I did run this on my own machine and I do see the initial blocking behaviour.
driis
@driis thank you
agend
@agend, Hejlsberg was wery specific about `async` and `await` _not_ automatically spinning up new threads. If you want a thread, you still have to start it yourself.
driis
@agend: I wouldn't say it is "blocking", rather, it is taking some time to run the code up to the first `await`. The method is declared `async` so it's given the opportunity to be asynchronous when it reaches an `await` line. Until it reaches one, it is run synchronously.
Jeff M
right - but i belive DownloadStringTaskAsync() - should start a new thread/task (and it does - right ?) - and do it in a way that doesn't block the calling thread for even one second - do you agree with me? i'm not saying the new keywords are not working - i'm saying DownloadStringTaskAsync() is not working properly.
agend
@agend I think your understanding is incorrect - **DownloadStringTaskAsync does not start a new thread** Please see the last paragraph I added to my answer.
driis
@Jeff M - that's what i call blocking - am i wrong?
agend
i'm gettin lost - but how can 2 things work at the same time (the counter in Main and page loading in DownloadStringTaskAsync() if there isn't another thread/task? What was the need for the new DownloadStringTaskAsync() method - there was one synchoronus one already.
agend
@agend, async and await amounts to calling the old async API's, but with syntactic sugar added to make it easier for you to write correct programs. Your DownloadStringTaskAsync blocks for a while, but not for the _whole_ time the request takes to complete. In other words, while the bits travel back and forth on the network, and the remote server replies, your thread is free to do some counting in your Main. But it blocks until the request has been prepared and sent to the underlying network API. This is exactly the behaviour we are seeing with both your original program, and my example above.
driis
@driis, you said : DownloadStringTaskAsync sets up a continuation - as far as i know it is the "await" and compiler behind which sets up the continuation. And i think there must be 2 threads. Anyway - thanks for your time. Arek
agend
@agend: See Anders Hejlsberg's talk for more information. He illustrates how the control flow goes at [11:30](http://player.microsoftpdc.com/Session/1b127a7d-300e-4385-af8e-ac747fee677a/690.552) and [38:20](http://player.microsoftpdc.com/Session/1b127a7d-300e-4385-af8e-ac747fee677a/2298.033).
Jeff M
@jeff M : i've seen it twice - and ?
agend
@agend, technically, there are two threads in play, because deep in the bowels of HttpWebRequest.BeginGetRequest (which is used by WebClient), a thread is used to wait for the task to complete. But that is the exact same behaviour whether you are using `async` keyword or the old fashioned async API. You are not creating new threads, and the `async` keywords does not create new threads - so DownloadStringTaskAsync runs HttpWebRequest.BeginGetReponse, on your thread, which takes time to complete (since it initiates the connection and pushes the request bits onto the network) ...
driis
... try to compare the program I posted to your async version. You will find they behave exactly the same - which is the point of `async`. It makes it *easier* to write *async* code, but there are no *black magic* that makes things run on extra threads.
driis
@driis: you said : "@agend I think your understanding is incorrect - DownloadStringTaskAsync does not start a new thread" - now you say it does start a new thread - but it does it "deep in bowels" - am i right? And did i say that async keyword start new threads? All i'm saying is WebClient.DownloadStringTaskAsync() works not exactly like it should - do you agree with me?
agend
The point is that DownloadStringTaskAsync itself does not start a thread, but it uses an existing async API, which at some point make usage of a thread to wait for an IO operation. This existing async API does some of it's work before control is returned. This is the delay you are seeing. So, no, I don't agree with you. I think DownloadStringTaskAsync works exactly as intended by the designers.
driis
A: 

Are you pressing F5 or CTLR+F5 to run it? With F5 there's a delay for VS just to search for the symbols for AsyncCtpLibrary.dll...

Lucian Wischik - MSFT
f5 or ctrl+f5 doesn't matter - works the same - the delay is about 5s so i don't think it's a symbols matter. I have the same behavior in wpf netflix_async_with_await example u get with cts.
agend
silverlight version of this example seems to be working fine - it doesn't freeze initially - but for some reason it doesn't display the movies icons either
agend
When you run a silverlight project locally, it will only show the images if it's part of an ASP project/website.
Lucian Wischik - MSFT
@agend, I do not see blocking behavior on my computer. Another possibility is that you have some kind of a proxy or firewall that makes it take time to initiate the request.
Lucian Wischik - MSFT
ok - in the silverlight version when i right click on TutoralCSTestPage -> view in browser - everything is working fine - no freezing, i can see the pictures. When i start the app with run (f5 or ctrl+f5 and startup project set on TutorialCS (silverlight app)) i can't see the pictures - does anybody know why - seems totally strange to me.
agend
@lucian - it wasn't only me - driis - had the same behavior on his computer, and i haven't any proxy. Why would my firewall freeze the application for 5 seconds - shouldn't it block everything else?
agend