如何启动线程以保持GUI刷新?

时间:2010-12-21 07:34:50

标签: c# wpf multithreading

我有带按钮的窗口,触发冗长的处理。我把处理放在一个单独的线程中,但是 - 令我惊讶的是 - 无论如何它都会冻结GUI。没有控制刷新,我甚至无法移动窗口。

所以问题是如何启动线程,因此它不会干扰GUI,即GUI总是最新的(在处理时我更改数据和GUI显示它的一些部分)?

这就是我当前开始线程的方式:

        var thread = new Thread(doLearn);
        thread.IsBackground = true;
        thread.Start();

编辑1

强:

  1. 我根本不使用任何锁
  2. 没有加入呼叫
  3. UI线程保持不变 - 它只是坐在那里
  4. 处理是一个带有数学运算的大循环,甚至没有分配内存,在UI端我有控件绑定(WPF)到数据,比如主循环的当前迭代次数。每次主循环“滴答”时都应该刷新它。循环的计数器是一个属性,它会在每次更改时触发OnPropertyChanged(经典的WPF绑定)。

    编辑2 - 几乎就在那里!

    好的,所以乔恩击中头部(谁是惊喜?;-D) - 谢谢!问题来自更换计数器。当我使用计数器时,本地计数器GUI被刷新 - 我的意思是我可以移动窗口,但是......我看不到计数器的显示。

    我在这里 - 一个带有这种数据绑定的WPF GUI

    <TextBlock Text="{Binding Path=Counter"/>
    

    我当然有Counter属性,每次更改都会发送事件PropertyChanged。其中一个听众肯定是GUI。

    所以,Jon的回答是有效的“答案”,但是从好的设计POV来看并不完全,因为如果GUI部分必须拉出有关Counter的信息并且每次(比方说)更新显示3秒,为什么会有人使用数据绑定?对我来说,这种方法使数据绑定理念无效。

    理论上,我可以将GUI调度程序传递给处理线程,并在GUI线程中执行所有发送,并且它可以工作(我没有尝试过)但是这意味着非GUI部分的紧密耦合GUI部分。

    到目前为止,我不知道如何以“正确”的方式做到这一点。到目前为止,最好的猜测是创建TimerDispatcher,但不是在GUI端,而是在处理库内,并立即更新Counter值,但不时地进行所有发送(尽管我还没有尝试)。

    小注释:我实际上有更多属性绑定,比如IsRunning,它在处理的开始和结束时都会被更改。这些更改会正确影响显示 - 但计数器更改会在3-4秒内触发大约3000个通知。所以看起来像是干扰问题。我做了另一个测试 - 我部分杀死了数据绑定,因此发送了通知,但GUI没有“接收”它们 - 但正在听它们。在这种情况下,GUI也被冻结。

    所以,我仍在听取所有建议 - 谢谢你提前分享。

    编辑3

    传奇继续在这里:

    How to do the processing and keep GUI refreshed using databinding?

4 个答案:

答案 0 :(得分:3)

有许多方法可以在UI线程上运行函数,但最简单且通常最合适的方法是查看BackgroundWorker组件。可以找到许多体面的教程。例如,here

答案 1 :(得分:3)

应该没问题。可能会冻结你的UI的事情:

  • 您是否在UI线程中锁定,并锁定其他线程中的同一个锁?
  • 您是否在UI线程中调用线程上的Join
  • 你是否在UI线程中做了一些其他繁重的工作?

如果你能想出一个显示问题的简短但完整的程序,我相信我们可以帮忙修复它......但它肯定应该没问题。

编辑:好的,现在你已经添加了这个:

  

循环的计数器是一个属性,它会在每次更改时触发OnPropertyChanged(经典的WPF绑定)。

所以你要从非UI线程更新属性?我希望这会导致问题,因为它会从错误的线程触发UI更改。

我建议您采取以下方法:

  • 通过Dispatcher.BeginInvoke
  • 定期更新计数器
  • 拥有“UI计数器”和“工作人员计数器” - 并通过DispatcherTimer将“工作人员计数器”中的值复制到UI线程中的“UI计数器”,基本上是轮询它。

答案 2 :(得分:0)

  

我把处理单独放在一起   线程,但 - 令我惊讶的是 - 它   无论如何都会使GUI冻结。

我真的不想告诉你,但是你没有把它放到一个单独的线程中。那个simlpe。

这里有一张海报,前面有一个类似的问题,并且在调用代码时出错,他在线程开始之前基本完成了所有处理,线程jsut返回结果。

答案 3 :(得分:0)

我遇到了同样的情况,并通过两种方式解决了......

  1. 使用其他类中的线程,并通过在其构造函数或任何方法中创建Thread来在主应用程序中调用它。

  2. 如果你想在同一个类中执行它,那么创建一个调用你的函数的Thread,该函数应该调用Delegate。

  3. 参见示例:

         public partial class Form1 : Form
        {
            private delegate void TickerDelegate();
            TickerDelegate tickerDelegate1;
    
            public Form1()
            {
                InitializeComponent();
            }
       //first solution
       // This button event call other class having Thread
    
           private void button1_Click(object sender, EventArgs e) 
            {
                f = new FormFileUpdate("Auto File Updater", this);
                f.Visible = true;
                this.Visible = false;         
            }
    
            // Second Solution
            private void BtnWatch_Click(object sender, EventArgs e)
            {
                tickerDelegate1 = new TickerDelegate(SetLeftTicker);
                Thread th = new Thread(new ThreadStart(DigitalTimer));
                th.IsBackground = true;
                th.Start();
             }
    
            private void SetLeftTicker()
            {
                label2.Text=DateTime.Now.ToLongTimeString();
            }
    
    
            public void DigitalTimer()
            {
                while (true)
                {
                    label2.BeginInvoke(tickerDelegate1, new object[] {});
                    Thread.Sleep(1000);
                }
            }
        }