C#表单:progressbar不显示正在执行工作的线程的更新

Eli*_*gne 2 .net c# forms user-interface

问题

如何在一个线程中工作并更新主线程上的进度条?

摘要

以下是我尝试解决问题,但它无法正常工作.我相信原因是更新在Windows消息泵中排队,但是所有这些都立即执行,所以它们发生得太快,用户看不到它们.

细节

我正在尝试创建一个GUI应用程序,它将在后台运行线程并更新进度条.我意识到使用backgroundworkerprocess会有更简单的时间,但这会带来我需要的一些灵活性.

我正在使用InvokeRequired和回调函数来处理从工作线程到主线程的调用,因此线程安全不应成为问题.

在我的电脑上,当我点击button1时,工作线程需要大约5秒才能完成其工作,并将结果输出到textbox1.在此期间,工作线程对updateProgressBar函数进行大约100次调用.此功能正在执行:

progressBar1.Value = percent;
progressBar1.Update();
progressBar1.Refresh();
progressBar1.Invalidate();
Run Code Online (Sandbox Code Playgroud)

但是,用户从未看到这些命令的影响.我相信他们都被放入消息泵来更新GUI,但他们只是排队等候并立即执行.

我已经尝试插入睡眠并玩弄正在进行的工作流程,但这没有帮助.如何刷新进度条更新?

需要注意的是,这是一个用来说明问题的人为例子.

// Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Text.RegularExpressions;

namespace ProgressBarExample
{
    public partial class Form1 : Form
    {
        Thread processThread;
        delegate void updateGUICallback(int read, int unread);
        delegate void updateProgressBarCallback(int percent);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string input = "abc1234 123456 domain\abc1234 123 0\r\n";

            // Create 1048576 lines of input
            for (int i = 0; i < 20; i++)
                input += input;

            processThread = new Thread(new ParameterizedThreadStart(processData));
            processThread.Start(input);
        }

        private void processData(object input)
        {
            // Break the input on newlines
            string[] lines = Regex.Split((string)input, "\r\n");

            int readLines = 0;
            int unreadLines = 0;
            int maxLines = lines.Length;


            // Setup for regex
            Match match;
            string pattern = @"^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$";

            // Read each line of input
            for (int i = 0; i < maxLines; i++)
            {
                // Attempt a regex match
                match = Regex.Match(lines[i].Trim(), pattern);

                // If the line matches our expected entry, add it to the list
                if (match.Success)
                {
                    readLines++;
                }
                else
                {
                    unreadLines++;
                }

                // Update the progressbar every 10000 iterations
                if (i % 10000 == 0)
                {
                    updateProgressBar(i / maxLines);
                }
            }

            updateTextbox(readLines, unreadLines);
        }

        private void updateTextbox(int read, int unread)
        {
            if (textBox1.InvokeRequired)
            {
                updateGUICallback cb = new updateGUICallback(updateTextbox);
                this.Invoke(cb, new object[] { read, unread });
            }
            else
            {
                textBox1.Text = "Read: " + read + "\r\n" +
                    "Unread: " + unread;
            }
        }

        private void updateProgressBar(int percent)
        {
            if (progressBar1.InvokeRequired)
            {
                updateProgressBarCallback cb = new updateProgressBarCallback(updateProgressBar);
                this.Invoke(cb, new object[] { percent });
            }
            else
            {
                progressBar1.Value = percent;
                progressBar1.Update();
                progressBar1.Refresh();
                progressBar1.Invalidate();
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

Ala*_*lan 5

这可能是你设想的例子中的巧合,但这条线

updateProgressBar(i / maxLines);
Run Code Online (Sandbox Code Playgroud)

应该真的读作

updateProgressBar(100 * i / maxLines);
Run Code Online (Sandbox Code Playgroud)

当您使用Windows窗体设计器的默认值时.我刚刚检查了修改和进度条定期更新,就像你(可能)想要的那样.

话虽如此,不得不在每个方法体前加上Invoke()或者BeginInvoke()苦差事,这实际上会很尴尬,除非你提到的灵活性是最重要的,否则我不会轻易地忽略BackgroundWorker方法.