locked
Wait task WebClient

    問題

  • Hi to everyone! I need to wait the WebClient operation(OpenReadTaskAsync) using AsyncCTP library.

    Now, I'm trying to do that like this, but it does not work. Any suggestions? Thank you very much!

    private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
            {
                BitmapImage src = this.GetImageAsync().Result;
    
                this.image1.Source = src;
    
                MessageBox.Show("Done!");
            }
    
            private async Task<BitmapImage> GetImageAsync()
            {
                WebClient client = new WebClient();
    
                BitmapImage img = new BitmapImage();
    
                img.SetSource(
                    await client.OpenReadTaskAsync(new Uri("http://www.website.com/image.png", 
                        UriKind.Absolute)));
    
                return await TaskEx.Run(delegate
                {
                    return img;
                });
            }

    2012年7月21日 上午 01:06

解答

  • You shouldn't mix asynchronous waiting (await) with synchronous waiting (Result). For one, it defeats the purpose on async, because it blocks a thread (in your case, it's the UI thread, you should never block that).

    But more importantly, doing that can also lead to deadlocks, as you observed. That's because the continuation of your GetImageAsync() method attempts to run on the UI thread, but the UI thread is blocked by your call to Result. So, the two pieces of code are waiting on each other and neither of them can continue.

    What you should do is to make your event handler method also async:

    private async void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
    {
        BitmapImage src = await this.GetImageAsync();
    
        this.image1.Source = src;
    
        MessageBox.Show("Done!");
    }

    Note that async void methods should be usually avoided, but handlers for UI events are one of the exceptions to this rule.

    For more information about deadlocks in async UI code, see Stephen Toub's article on the topic.

    Another possibility is to make sure the continuation of GetImageAsync() doesn't run on the UI thread, because it doesn't have to. To do that, you can use ConfigureAwait():

    img.SetSource(
        await client.OpenReadTaskAsync(new Uri("http://www.website.com/image.png", 
            UriKind.Absolute))
            .ConfigureAwait(false));

    The best option is to do both: use await in your event handlers and also use ConfigureAwait() in your library methods that don't need to access the UI.

    Also, the return in your GetImageAsync() using delegate is unnecessarily complex, you could simplify it to just:

    return img;
    • 已編輯 svick 2012年7月21日 上午 08:55
    • 已標示為解答 StefyGraf 2012年7月21日 下午 04:25
    2012年7月21日 上午 08:49