为什么WPF中的BackgroundWorker需要Thread.Sleep来更新UI控件?

发布于 2024-09-05 08:46:57 字数 2164 浏览 10 评论 0原文

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    BackgroundWorker bgWorker;
    Action<int> myProgressReporter;

    public Window1()
    {
        InitializeComponent();
        bgWorker = new BackgroundWorker();
        bgWorker.DoWork += bgWorker_Task;
        bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;

        // hook event to method
        bgWorker.ProgressChanged += bgWorker_ReportProgress;

        // hook the delegate to the method
        myProgressReporter = updateProgress;

        bgWorker.WorkerReportsProgress = true;

    }

    private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        object result;
        result = e.Result;
        MessageBox.Show(result.ToString());
        progressBar1.Value = 0;
        button1.IsEnabled = true;
    }

    private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
    {
        System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
        disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
        //Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
    }

    private void updateProgress(int progressPercentage)
    {
        progressBar1.Value = progressPercentage;
    }

    private void bgWorker_Task(Object sender, DoWorkEventArgs e)
    {
        int total = 1000;
        for (int i = 1; i <= total; i++)
        {
            if (bgWorker.WorkerReportsProgress)
            {
                int p = (int)(((float)i / (float)total) * 100);
                bgWorker.ReportProgress(p);
            }
            Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception, 
        }

        e.Result = "Completed";
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {            
        if(!bgWorker.IsBusy)
            bgWorker.RunWorkerAsync("This is a background process");
        button1.IsEnabled = false;
    }
}
}
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    BackgroundWorker bgWorker;
    Action<int> myProgressReporter;

    public Window1()
    {
        InitializeComponent();
        bgWorker = new BackgroundWorker();
        bgWorker.DoWork += bgWorker_Task;
        bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;

        // hook event to method
        bgWorker.ProgressChanged += bgWorker_ReportProgress;

        // hook the delegate to the method
        myProgressReporter = updateProgress;

        bgWorker.WorkerReportsProgress = true;

    }

    private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        object result;
        result = e.Result;
        MessageBox.Show(result.ToString());
        progressBar1.Value = 0;
        button1.IsEnabled = true;
    }

    private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
    {
        System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
        disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
        //Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
    }

    private void updateProgress(int progressPercentage)
    {
        progressBar1.Value = progressPercentage;
    }

    private void bgWorker_Task(Object sender, DoWorkEventArgs e)
    {
        int total = 1000;
        for (int i = 1; i <= total; i++)
        {
            if (bgWorker.WorkerReportsProgress)
            {
                int p = (int)(((float)i / (float)total) * 100);
                bgWorker.ReportProgress(p);
            }
            Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception, 
        }

        e.Result = "Completed";
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {            
        if(!bgWorker.IsBusy)
            bgWorker.RunWorkerAsync("This is a background process");
        button1.IsEnabled = false;
    }
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

噩梦成真你也成魔 2024-09-12 08:46:57

因为在您的(人为)场景中,您向主线程发送了 1000 个更新请求。
它没有时间进行空闲循环(需要进行屏幕更新)。

但是(感谢 TerrorAustralis),您应该从合并 bgWorker_ReportProgress 和 myProgressReporter 方法开始。您现在正在同步两次,这可能是导致 stackoverflow 的原因。调度 UpdateProgress 事件是Backgroundworker 的主要功能之一:

private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
  //System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
  //disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
  progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}

Because in your (artificial) scenario you pump 1000 request-for-update to the main thread.
It gets no time to do an idle loop (necessary to do a screen update).

But (thanks to TerrorAustralis), you should start with merging your bgWorker_ReportProgress and myProgressReporter methods. You are now synchronizing twice, a possible cause of stackoverflow. Dispatching the UpdateProgress events is one of the main features of the Backgroundworker:

private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
  //System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
  //disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
  progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}
神经暖 2024-09-12 08:46:57

可能性:
Dispatcher.BeginInvoke() 是一个异步操作。既然是这种情况,您可以在它完成操作之前再次尝试点击它。要查看这是否是问题所在,请尝试同步的 Dispatcher.Invoke()

作为一种可能的解决方法,如果您只想更新进度栏,backgroundWorker ProgressChanged 事件可以在不使用显式调度程序的情况下完成此操作。

Posibility:
Dispatcher.BeginInvoke() is an Asynchronous operation. Since this is the case, you are able to try to hit it again before it completes its operation. To see if this is the problem, try Dispatcher.Invoke() which is Synchronous

As a possible workaround, if you just want to update the progress bar, the backgroundWorker ProgressChanged event could do this without the use of an explicit dispatcher.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文