C#窗体:progressbar没有';t显示正在执行工作的线程的更新
本文关键字:执行 工作 线程 显示 更新 progressbar 窗体 没有 | 更新日期: 2023-09-27 18:29:34
问题
我如何在一个线程中工作并更新主线程上的进度条?
摘要
下面是我试图解决这个问题的方法,但没有奏效。我相信原因是更新在Windows消息泵中排队,但随后所有更新都同时执行,所以它们发生得太快了,用户看不到它们。
详细信息
我正在尝试创建一个GUI应用程序,它将在后台处理线程并更新进度条。我意识到使用backgroundworkerprocess会更简单,但这会占用我所需要的一些灵活性。
我使用InvokeRequired和回调函数来处理从工作线程到主线程的调用,因此线程安全性不应该成为问题。
在我的电脑上,当我点击按钮1时,工作线程大约需要5秒才能完成工作并将结果输出到文本框1。在此期间,工作线程对updateProgressBar函数进行大约100次调用。此功能正在执行:
progressBar1.Value = percent;
progressBar1.Update();
progressBar1.Refresh();
progressBar1.Invalidate();
但是,用户永远看不到这些命令的效果。我相信它们都被放入消息泵中以更新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();
}
}
}
}
在您设计的示例中,这可能是巧合,但行
updateProgressBar(i / maxLines);
应该读作
updateProgressBar(100 * i / maxLines);
当您使用Windows窗体设计器的默认值时。我刚刚检查了它的修改和进度条定期更新,正如你(可能)希望的那样
话虽如此,必须为每个方法体准备Invoke()
或BeginInvoke()
这项繁重的工作真的会很尴尬,除非你提到的灵活性至关重要,否则我不会这么容易就放弃BackgroundWorker方法。