1

Trying to use Async using Microsoft.Bcl on .net 4.0 in Visual Studio 2012. Results does not show up.

private void button3_Click(object sender, EventArgs e)
        {
            byte[] resultBytes;// = ReceiveStringAsync().Result;
            Task<byte[]>[] tasks = new Task<byte[]>[1];
            tasks[0] = ReceiveStringAsync();
            Task.WaitAll(tasks, -1);
            resultBytes = tasks[0].Result;
            MessageBox.Show("Async Mode called in sync - " + data.Length.ToString());
        }

        public async Task<byte[]> ReceiveStringAsync()
        {
            string strURL = @"https://testurl.com";
            WebClient client = new WebClient();
            byte[] data = await client.DownloadDataTaskAsync(strURL).ConfigureAwait(true);

            return data;
        }
Bojan
  • 59
  • 1
  • 9

1 Answers1

1

This is the problem, which has nothing to do with the fact that you're using .NET 4:

Task.WaitAll(tasks, -1);

That will block until everything in tasks completes. At that point, your UI thread can't do any more work... but it has to do more work in order to resume the code within ReceiveStringAsync after the await expression. So your task can't complete until Task.WaitAll has completed, which can't complete until the task has completed, and you have a deadlock.

The lesson here is never to use blocking calls like Task.WaitAll (or Task.Result or Task.Wait()) within the UI thread.

The solution is to make button3_Click async as well, so you can await the Task<byte[]> returned by ReceiveStringAsync.

private async void button3_Click(object sender, EventArgs e)
{
    byte[] resultBytes = await ReceiveStringAsync();
    // Not clear what data is here, but that's a different matter...
    MessageBox.Show("Async Mode called in sync - " + data.Length.ToString());
}

As an aside, it's really odd for a ReceiveStringAsync method to return a Task<byte[]> instead of a Task<string>...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • minor aside, but it is a *really* good idea for `async void` handlers to ensure they have some `try`/`catch` handling - very bad things otherwise – Marc Gravell Oct 13 '14 at 13:18
  • @MarcGravell: True - but do you have a link giving more details? (I'd rather not going into huge amounts of detail for the aside, but it would be nice to give further reading. Feel free to edit it straight in.) – Jon Skeet Oct 13 '14 at 13:19
  • @MarcGravell , Thanks for the correction, but I find that I cannot add await to button3_Click as it does not have an await. – Bojan Oct 13 '14 at 13:25
  • @BojanShivShanker if you look, Jon *added* `async` to the method declaration in his answer; you should therefore be able to `await`, assuming your compiler supports `async`/`await` – Marc Gravell Oct 13 '14 at 13:27
  • @MarcGravell, also, removing 'Task.WaitAll(tasks, -1)' does not help, infact I added it later, since the earlier code did not work. Any more thoughts? – Bojan Oct 13 '14 at 13:28
  • @BojanShivShanker I think you mean to be `@Jon` here, not me – Marc Gravell Oct 13 '14 at 13:28
  • Cool! Thanks to @JonSkeet adding async to button3_Click and await to ReceiveStringAsync() did the stuff. – Bojan Oct 13 '14 at 13:35
  • Thanks @MarcGravell, for making me realize that! – Bojan Oct 13 '14 at 13:35
  • @MarcGravell , I have a challenge here of calling the ReceiveStringAsync in different scenarios, one synchronlously and other asynchronously. I hope, calling Task.WaitAll in sync mode and do nothing for the async mode, will do the trick, while both calling the aync methods. Jon can you help. – Bojan Oct 14 '14 at 05:26
  • @Bojan: Well, I wouldn't use `Task.WaitAll` to wait for a single task - I'd just use `Task.Result`. But whether or not that's suitable depends on the context. It would be *better* just to use a synchronous method from `WebClient` in that situation. – Jon Skeet Oct 14 '14 at 05:50
  • @JonSkeet: That would mean having multiple methods doing the same task one for synchrononous mode and other for asynchronous mode. Can we not have one being used in both cases. Actually, I am having multiple huge file downloads, the download of which in sync mode will have to wait all along and in asyn, I want to have them in the background. – Bojan Oct 14 '14 at 06:06
  • @Bojan: Yes, it would mean multiple methods - because you're doing different things. You could normally have helper methods which are shared between the two approaches, leaving only a small amount of different code. There *are* ways of doing this, and in some cases you could just block on the task - but you really need to understand exactly what's going on in order to make sure you don't end up with a deadlock like you did in this question. – Jon Skeet Oct 14 '14 at 06:08
  • @JonSkeet: I am not sure if I should clarify this here, but I was not able to find a suitable answer elsewhere. How do I post to the page, I am downloading the files from or in other words how can I pass Method='Post' and header body with some xml file to pass to the web request before I download the files. – Bojan Oct 14 '14 at 13:47
  • You definitely shouldn't be asking here, as it's completely unrelated to the rest of the question. You should ask a new question - after looking very carefully at the `WebClient` API. You may need to use the lower-level `WebRequest` API instead. – Jon Skeet Oct 14 '14 at 13:55