简体   繁体   English

为什么这个异步方法会阻塞UI线程?

[英]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. 我在这里想要实现的是为XAML的WebView找到一个解决方法,我发现它非常草率。 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. 我希望能够在它上面绘制内容,但我不能这样做我基本上做的是重复拍摄WebView快照(使用WebViewBrush )(基于int FPS字段),然后设置Background属性带有此快照的名为“ webViewContent ”的画布。 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. 它现在工作正常(我成功地将任何Tapped事件重定向到WebView的内部,以便正确处理按钮/链接/ ...的点击),但它有点滞后。 The slow bit being wvb.Redraw() I was wondering how I could improve the performance of my thread. 慢点是wvb.Redraw()我想知道如何改善我的线程的性能。 It looks like the UI is responsive during the Task.Delay but is blocked otherwise... Task.Delay 期间看起来UI是响应的但是否则被阻止...

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): 编辑:这是我如何计算Redraw调用(我认为这是导致问题的原因,因为删除它会使应用程序响应非常快):

        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... 在Task.Delay期间看起来UI是响应的,但是否则被阻止...

Well yes. 嗯,是。 That's exactly what's happening. 正是发生的事情。 Task.Delay is the only chance you're giving the UI thread to work. Task.Delay是你让UI线程工作的唯一机会。 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. 您的异步方法正在UI线程上执行 - 一旦“延迟”任务完成,您将最终继续等待在UI线程上执行,该线程将重绘然后再次延迟。

Fundamentally, if your Redraw method is too slow to be called ~60 times per second, you need a different approach. 从根本上说,如果您的Redraw方法太慢而无法每秒调用~60次,则需要采用不同的方法。

It's very important to understand that async doesn't put the method onto a different thread - it just allows you to act asynchronously. 理解async 不会将该方法放在不同的线程上非常重要 - 它只允许您异步操作。 (Your description and title suggest that you expected your method not to be using the UI thread for any significant time.) (您的描述和标题表明您希望您的方法在任何重要时间都不使用UI线程。)

Additionally, as Stephen Cleary says, using a DispatcherTimer is a generally-better way of executing code periodically on the UI thread. 此外,正如Stephen Cleary所说,使用DispatcherTimer是在UI线程上定期执行代码的一种更好的方法。

Task.Delay is not particularly efficient for repeatedly doing many short timeouts. Task.Delay对于重复执行许多短暂超时并不是特别有效。 You'll generate a lot of garbage. 你会产生很多垃圾。

I would recommend using a dispatcher timer or similar for this scenario. 我建议在这种情况下使用调度程序计时器或类似计时器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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