【问题标题】:WPF UI Freezes - UI Thread conflict?WPF UI 冻结 - UI 线程冲突?
【发布时间】:2015-05-28 23:32:05
【问题描述】:

我正在尝试使用WPF 创建图像幻灯片效果。
Windows.Forms.Timer 中每隔几秒调用一次使用新图像更新幻灯片的方法,并在Task 中的自己的线程中运行(如下所示)。

private void LoadImage()
    {
        Task t = Task.Run(() =>
        {    
           this.Dispatcher.BeginInvoke((Action)(() =>
           {
               TimeSpan delay = new TimeSpan(0, 0, 0, 0, 0);
               Fader.ChangeSource(image, BitmapFromUri(new Uri(compPath + oComps[nCount].name)), delay, delay);
               image.Visibility = System.Windows.Visibility.Visible;

               mediaElement.Stop();
               mediaElement.Close(); ;
               mediaElement2.Stop();
               mediaElement2.Close();
               mediaElement.Visibility = System.Windows.Visibility.Collapsed;
               mediaElement2.Visibility = System.Windows.Visibility.Collapsed;

               imageLoop.Interval = oComps[nCount].duration;

               nCount++;

               imageLoop.Start();
           }));
        });
    }

同时,画布底部有一个滚动文本横幅,位于叠加层中。这也在它自己的线程中运行,通过Dispatcher 更新 UI。

每隔几张图片,滚动文本幻灯片都会暂停一两秒,似乎在等待图片加载。这种行为是出乎意料的,因为每个元素都在一个单独的线程中。


这可能是两个更新 UI 线程的任务线程之间的冲突吗?
可能是什么原因造成的?

【问题讨论】:

  • 您是否检查了图像加载请求何时发送到服务器,例如。 G。通过Fiddler?
  • 顺便说一句,您为什么将System.Windows.Forms.TimerWPF 一起使用?使用System.Windows.Threading.DispatcherTimer。此外,您在发布内容的第 12 行有一个额外的分号。此外,尝试在调用之外尽可能多地执行代码,因为那部分是阻塞的。最后,不要使用new TimeSpan(0,0,0,0),而是使用TimeSpan.Zero。考虑使用async/await

标签: c# wpf multithreading


【解决方案1】:

您将工作放在另一个线程上的代码不会将工作放在另一个线程上。您的 BeginInvoke 正在将其发送回 UI 线程,您的所有工作都在那里完成。

在调用 BeginInvoke 之前先完成繁重的工作,这样工作才能真正发生在后台线程上。

private void LoadImage()
{
    Task t = Task.Run(() =>
    {    
       //I assume BitmapFromUri is the slow step.
       var bitmap = BitmapFromUri(new Uri(compPath + oComps[nCount].name);

       //Now that we have our bitmap, now go to the main thread.
       this.Dispatcher.BeginInvoke((Action)(() =>
       {
           TimeSpan delay = new TimeSpan(0, 0, 0, 0, 0);

           //I assume Fader is a control and must be on the UI thread, if not then move that out of the BeginInvoke too.
           Fader.ChangeSource(image, bitmap), delay, delay);
           image.Visibility = System.Windows.Visibility.Visible;

           mediaElement.Stop();
           mediaElement.Close(); ;
           mediaElement2.Stop();
           mediaElement2.Close();
           mediaElement.Visibility = System.Windows.Visibility.Collapsed;
           mediaElement2.Visibility = System.Windows.Visibility.Collapsed;

           imageLoop.Interval = oComps[nCount].duration;

           nCount++;

           imageLoop.Start();
       }));
    });

我怀疑你的横幅实际上也没有在另一个线程上工作,你可能想看看它。


如果可能的话,一个更好的解决方案是将 BitmapFromUri 重写为异步并且根本不使用线程。

private async Task LoadImageAsync()
{
   TimeSpan delay = new TimeSpan(0, 0, 0, 0, 0);      

   var bitmap = await BitmapFromUriAsync(new Uri(compPath + oComps[nCount].name);
   Fader.ChangeSource(image, bitmap), delay, delay);
   image.Visibility = System.Windows.Visibility.Visible;

   mediaElement.Stop();
   mediaElement.Close(); ;
   mediaElement2.Stop();
   mediaElement2.Close();
   mediaElement.Visibility = System.Windows.Visibility.Collapsed;
}

【讨论】:

  • async/await 绝对是要走的路。很好地简化和清理了事情。我现在不能回到以前的方式了。
猜你喜欢
  • 2013-11-28
  • 1970-01-01
  • 2015-01-16
  • 2011-06-22
  • 2021-12-21
  • 1970-01-01
  • 1970-01-01
  • 2016-05-12
  • 1970-01-01
相关资源
最近更新 更多