我遇到了一个小问题。我有两个线程,一个执行一个循环,每次都需要返回/发送一个数字到GUI的线程。为此,我使用 BackGroundWorker 和 ReportProgress 。
让我们这样说:
我有一个 BackGroundWorker 执行(DoWork)一个从0到任何数字的简单循环。循环的每个条目我使用ReportProgress事件将计数器发送到GUI的线程,该线程将打印计数器的值。
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int count = 0;
BackgroundWorker Worker = (BackgroundWorker)sender;
while (count < 10000000)
{
Worker.ReportProgress(count);
count++;
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
txt.Text = e.ProgressPercentage.ToString();
}
现在,这个操作冻结了GUI。
我知道ReportProgress正在创建 BackGroundWorker 的线程中调用ProgressChange处理程序,所以我认为循环执行速度太快,因此GUI的线程没有成功将值打印为必需的。
如果不冻结GUI,我该怎么办才能执行这样的任务?
我听说过Dispatcher,但我不确定它用于什么。
答案 0 :(得分:3)
问题是每次更改时都在调用reportProgress。只有在“需要”报告进度时才应调用它。请参阅MSDN http://msdn.microsoft.com/en-us/library/ka89zff4.aspx。 把你的笨蛋变成这样的东西:
while (count < 10000000)
{
if ((count % 1000) == 0)
Worker.ReportProgress(count);
count++;
}
这会在每1000个处理过的项目之后调用ReportProgress,因此不会给GUI线程带来不必要的负担
答案 1 :(得分:1)
您的示例代码尝试以比GUI处理更新通知消息快得多的速度更新GUI,因此使用gunge充斥GUI Windows消息队列并阻止它处理其他消息 - GUI冻结。
监视非GUI线程中高速率操作的进度是轮询是更好解决方案的少数几次之一。使用Forms.Timer事件来读取和显示'currentProgress'值,可能由线程的方法返回。 500毫秒是一个合理的计时器值 - 人类用户无法在编辑/文本框中跟上变化的整数值,速度要快得多。
'理想情况下',currentProgress值的读/写应该被锁定,也许是使用原子操作,但是如果你每500ms只读一个int,你可能甚至不需要那个'真实' '线程的功能意味着进度计数不太可能连续缓存在寄存器中。
答案 2 :(得分:0)
如何在不冻结GUID的情况下执行此类任务? :
使用调度程序让我假设您正在使用WPF,无论如何,它将是:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int count = 0;
BackgroundWorker Worker = (BackgroundWorker)sender;
while (count < 10000000)
{
Worker.ReportProgress(count);
count++;
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() => { txt.Text = e.ProgressPercentage.ToString(); }));
}
调用Dispatcher.BeginInvoke会导致在UI线程上实际执行给定的Action,确保除了访问UI元素的UI线程之外的线程的原因不会引发异常。
此外,您可以尝试这样做,只是作为替代by using a task.