更新C#WPF应用程序计时任务

时间:2015-10-17 21:09:37

标签: c# wpf multithreading

问题非常简单:我需要更新WPF应用程序的进度,同时处理耗时的计算。在我的尝试中,我做了一些谷歌搜索,最后基于此解决方案的第一个代码片段:How to update UI from another thread running in another class。这是我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Threading;

namespace ThreadTest
{
    public class WorkerClass
    {
        public int currentIteration;
        public int maxIterations = 100;

        public event EventHandler ProgressUpdate;

        public void Process()
        {
            this.currentIteration = 0;
            while (currentIteration < maxIterations)
            {
                if (ProgressUpdate != null)
                    ProgressUpdate(this, new EventArgs());

                currentIteration++;
                Thread.Sleep(100); // some time-consuming activity takes place here
            }
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            WorkerClass wItem = new WorkerClass();

            wItem.ProgressUpdate += (s, eArg) => {
                Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); });
            };
            Thread thr = new Thread(new ThreadStart(wItem.Process));
            thr.Start();

            // MessageBox.Show("Job started...");

            while (thr.IsAlive == true)
            {
                Thread.Sleep(50); 
            }

            MessageBox.Show("Job is done!");
        }
    }
}

问题在于,如果我使用Dispatcher.Invoke,则工作线程(thr)在第一个循环传递后进入WaitSleepJoin状态并且不会恢复,因此整个应用程序冻结。我已经搜索了几个使用Dispatcher.BeginInvoke的建议,但在这种情况下,进度不会更新,直到该过程完成工作。我想这个问题与线程之间的切换有关,但是无法得到准确的点。

2 个答案:

答案 0 :(得分:1)

这是一个经典的&#34;调用死锁&#34;场景。 Stack Overflow有许多解决Winforms问题的现有问题,但我只能在WPF上下文中找到一个相关的问题(Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish),但这个问题不是完全相同的问题,或者至少是没有答案(还)会直接解决你的问题,问题和答案本身也很差,我觉得你的问题需要一个新的答案。所以......


基本问题是您已阻止等待进程完成的UI线程。你应该从不这样做。您应该从不以任何理由阻止UI线程。很明显,如果你在UI线程中运行的代码中等待任何原因,那么UI本身就无法响应用户输入或进行任何类型的屏幕刷新。

有很多可能的方法可以解决这个问题,但目前解决此问题的方法是使用asyncawait以及Task来运行您的流程。例如:

private async void btnStart_Click(object sender, RoutedEventArgs e)
{
    WorkerClass wItem = new WorkerClass();

    wItem.ProgressUpdate += (s, eArg) => {
        Dispatcher.BeginInvoke((Action)delegate() { txtProgress.Text = wItem.currentIteration.ToString(); });
    };

    Task task = Task.Run(wItem.Process);

    // MessageBox.Show("Job started...");

    await task;

    MessageBox.Show("Job is done!");
}

请注意,虽然上述内容将async方法声明为void,但这是规则的例外情况。也就是说,通常应该将async方法声明为返回TaskTask<T>。例外情况是这样的情况,其中方法是事件处理程序,因此需要匹配现有签名,其中必须声明方法以返回void

答案 1 :(得分:1)

我按原样运行你的代码,并注释掉了这个:

while (thr.IsAlive == true)
            {
                Thread.Sleep(50); 
            }

一切都按预期工作。

/ 用户评论后编辑 /

要获得处理完成的通知,请执行以下更改:

一个。您的WorkerClass中的public event EventHandler ProgressCompleted;

if (ProgressCompleted != null)
       ProgressCompleted(this, new EventArgs());
在您完成Process()方法之后

℃。在创建线程之前的BtnStart_Click中。

wItem.ProgressCompleted += (s1, eArgs1) =>
            {
                MessageBox.Show("Job is done!");
            };