2012-11-04 34 views
0

我遇到了一个小问题。我有两个线程执行循环,每次都需要返回/发送一个数字到GUI的线程。为此,我使用BackGroundWorkerReportProgress简单的任务没有冻结gui

让我们说这样的事情:

我有一个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(); 
    } 

现在,这个opretion冻结了GUI。

我知道ReportProgress在创建BackGroundWorker的线程上调用ProgressChange处理程序,所以我认为循环执行速度如此之快,因此GUI线程无法按要求打印值。

如何在不冻结GUI的情况下执行类似的任务?

我听说过Dispatcher,但我不确定它的用途。

+2

你的意思是GUI的权利?因为GUID是完全不同的东西,据我所知:-)另外,你是否使用WinForms?或者我可以假设WPF? – Daneo

+0

'GUI' =图形用户界面; 'GUID' =全局唯一标识符...仅供参考;) –

回答

3

问题在于,每当有事情发生变化时,您都会调用reportProgress。只有当您需要报告进度时,您才应该调用它。请参阅MSDN http://msdn.microsoft.com/en-us/library/ka89zff4.aspx。 你的DoWork更改为这样的事情:

while (count < 10000000) 
    { 
     if ((count % 1000) == 0) 
      Worker.ReportProgress(count); 
     count++; 
    } 

这将每1000个处理的项目之后,调用ReportProgress,因此不要把不必要的负载,以您的GUI线程

+0

是的,我意识到这一点..但是这不是我的原始循环,我的原始循环做了比这更大的东西,需要返回一个值到GUID的线程。 – Matan

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线程上执行,使得当然,除了UI线程访问UI元素之外,不会引发异常。

此外,你可以试试这个,只是作为一种替代by using a task.

+1

ProgressChanged事件处理程序在创建Background Worker的线程中调用。但即使它不是在UI线程中创建的,它也会引发异常,而不会阻止UI。 – Ondra

+0

这并没有奏效,但我不明白你为什么选择这样做? – Matan

+0

@Matan我正在考虑线程安全性,调用调度程序使100%确定操作在UI线程上执行。如果你在循环中放入一个Thread.Sleep(500),你是否看到相应的UI更新? – Daneo

1

您的示例代码试图在比GUI更快的速度来更新GUI可以处理的更新通知消息,所以充斥GUI的Windows消息与gunge队列并阻止它处理其他消息 - GUI冻结。

监控非GUI线程中高速率操作的进度是轮询更好的解决方案时的少数几次之一。使用Forms.Timer事件来读取并显示'currentProgress'值,可能由线程的方法返回。 500ms是一个合理的计时器值 - 人类用户无法跟上编辑/文本框中不断变化的整数值,其速度远远快于此值。'理想情况下',应该锁定currentProgress值的读/写,可能是使用原子操作符,但是如果每500ms只读一个int值,那么如果'线程的“真实”功能意味着进度计数不太可能连续缓存在寄存器中。

+0

谢谢,这也是我的想法,所以最后一件事,创建backgroundWorker的线程是否可以读取进度计数器? – Matan

+0

我的意思是,创建backgroundworker的线程可以访问属于backgroundworker线程的currentProgress值吗? – Matan