如何在 C# 中同时使用进度条、后台工作程序、窗口窗体
本文关键字:后台 工作程序 窗体 窗口 | 更新日期: 2023-09-27 18:35:23
我遇到了一个问题,即我正在使用Backgroundworker 在进度条中显示我的工作进度。用于后台工作者的代码:-
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(200);
for (int i = 0; i <= 100; i++)
{
Delegate del= new DELEGATE(simulateHeavyWork);
this.Invoke(del);
backgroundWorker1.ReportProgress(i);
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
backgroundWorker1.ReportProgress(0);
return;
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
percentLabel.Text = e.ProgressPercentage.ToString() + "%";
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Cancelled");
}
else
{
MessageBox.Show("Completed");
}
}
我已经在代码上创建了一个委托:-
public partial class Form1 : Form
{
private delegate void DELEGATE();
public Form1()
{
InitializeComponent();
}
private void simulateHeavyWork()
{
Thread.Sleep(100);
...lines of code to perform some search logs operation..
....
}
}
我想要实现的功能是进度条应该报告我的函数 simulateHeavyWork() 的进度,该函数实际上正在使用 UI 线程,因为它需要从我的表单控件中获取输入并更新它。
现在正在发生的问题是代码实际上正在调用 simulateHeavyWork() 并提供正在更新 UI 控件并且工作完成的输出。(注意:我在这里使用了委托以避免在 ui 线程上运行的错误交叉控件,因为我的函数需要使用 UI 控件。
完成后,它开始更新进度条,这是错误的,看起来它一次又一次地调用 simulateHeavyWork,睡眠间隙 (100)。
user3222101,正如Andy之前所说,你正在连续运行simulateHeavyWork()。此外,通过调用 Invoke,您将在 UI 线程中运行此方法,这会导致 UI 线程中的额外睡眠。基本上,Invoke 使用使用它的控件的消息循环(泵)(在本例中为 Form1),并将委托放入 UI 线程的队列中以便执行。我认为这不是一个好的做法,因为 Sleep() 调用和 simulateHeavyWork() 方法中的耗时日志操作。
我希望,清楚地了解你的问题。我的建议是将耗时的日志操作与 UI 线程分开。不要将 UI 线程的宝贵时间花在缓慢而无聊的 I/O 操作上。从控件中获取值(在BackgroundWorker
中使用 Invoke,我将在下面解释),在BackgroundWorker
中执行任何您想要的操作并更新您的 GUI(再次使用 Invoke),而无需接触此类繁重任务的 UI 线程。
正如 Andy 建议的那样,您可以通过 RunWorkerAsync
参数传递数据,并且应该创建一个可以存储您需要的任何数据的类(因为它只接受一个参数)。但是,您可以随时使用 Invoke 从另一个线程中获取表单中的值。调用方法还返回委托中的值(请参阅下面链接中的示例),这使您有机会在窗体上获取控件的值。创建一个委托,该委托返回您为 RunWorkerAsync
创建的类类型的对象,并在 BackgroundWorker
线程中使用此值。请看一下这里的例子。
public static string GetTextThreadSafe(this TextBox box)
{
return GetTextBoxText(box);
}
此外,示例使用 Func<...> 来返回值。
通过这种方式,您可以(在线程中)休眠一段时间(BackgroundWorker
然后从控件中获取值(当前值)并执行任何您想做的事情(再次BackgroundWorker
线程中)。我认为,这改进了您的代码。
从你的问题:"这是错误的,看起来它一次又一次地调用模拟沉重的工作与睡眠(100)的间隙。
当然叫。只需查看您的代码:
for (int i = 0; i <= 100; i++)
{
Delegate del= new DELEGATE(simulateHeavyWork);
this.Invoke(del);
所以你在这里给simulateHeavyWork
打电话100次。而且由于您在simulateHeavyWork
正文中输入了Thread.Sleep(100);
- 呼叫之间的差距约为Sleep(100)