简体   繁体   中英

Why is this async method blocking the UI thread?

I am struggling with an issue which is raised by this piece of code:

    private int FPS = 60;

    void WebView_LoadCompleted(object sender, NavigationEventArgs e)
    {
        WebviewContentWorker();
    }

    private async void WebviewContentWorker()
    {
        WebViewBrush wvb = new WebViewBrush();
        wvb.SetSource(WebView);
        wvb.Redraw(); //we must redraw at least once before collapsing the WebView
        WebView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

        while (true)
        {
            webViewContent.Background = wvb; //webViewContent is a canvas
            await Task.Delay(1000 / FPS);
            wvb.Redraw();
        }
    }

What I'm trying to achieve here is to find a workaround for XAML's WebView which I find very sloppy. I want to be able to draw things on top of it but I can't so what I'm basically doing is taking snapshots of the WebView (using the WebViewBrush ) repeatedly (based on the int FPS field) and then setting the Background property of the canvas named " webViewContent " with this snapshot. The aim is to have animations displayed on the canvas while still being able to draw on top of it (if I don't do these fast snapshots, the canvas will display a still image).

It is working fine now (I successfully redirect any Tapped event to the inside of the WebView so that clicks on buttons/links/... are properly handled) but it is somehow laggy. The slow bit being wvb.Redraw() I was wondering how I could improve the performance of my thread. It looks like the UI is responsive during the Task.Delay but is blocked otherwise...

Any input/advice is very welcome!

Edit: Here is how I timed the Redraw call (which I believe is what causes the problem since removing it makes the application very responsive):

        while (true)
        {
            webViewContent.Background = wvb;
            await Task.Delay(1000 / FPS);
            sw.Reset();
            sw.Start();
            wvb.Redraw();
            sw.Stop();
            System.Diagnostics.Debug.WriteLine(sw.Elapsed.TotalMilliseconds);
        }

Which gives me these results in the output window:

0,094
0,058
0,041
0,053
0,057
0,038
0,032
0,033
0,032
0,038
0,035
0,03
0,042
0,028
0,044
0,031
0,033
0,029
0,034
0,03
0,052
0,029

So not so much after all...

It looks like the UI is responsive during the Task.Delay but is blocked otherwise...

Well yes. That's exactly what's happening. Task.Delay is the only chance you're giving the UI thread to work. Your asynchronous method is executing on the UI thread - as soon as the "delaying" task completes, you'll end up with a continuation waiting to execute on the UI thread which will redraw and then delay again.

Fundamentally, if your Redraw method is too slow to be called ~60 times per second, you need a different approach.

It's very important to understand that async doesn't put the method onto a different thread - it just allows you to act asynchronously. (Your description and title suggest that you expected your method not to be using the UI thread for any significant time.)

Additionally, as Stephen Cleary says, using a DispatcherTimer is a generally-better way of executing code periodically on the UI thread.

Task.Delay is not particularly efficient for repeatedly doing many short timeouts. You'll generate a lot of garbage.

I would recommend using a dispatcher timer or similar for this scenario.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM