.Net2.0工作线程和MFC8.0托管支持
【IT168 技术文档】
在计算亲和数的时候,由于涉及到密集运算,有必要把计算工作转移到背景线程,以避免界面会失去响应。在.Net 1.0中,可以用ManualResetEvent、线程和Delegate的异步调用来实现,但是在.Net 2.0中,可以使用BackgroundWorker对象来简化这个工作。这个对象自动化了进度报告和终止线程的功能。
要使用这个对象来创建工作线程,首先需要加入一个BackgroundWorker对象到表单(Form)或者用户控件(UserControl),然后调用其RunWorkerAsync方法:
private: System::Void AmicableNumberView_Load(System::Object^ sender, System::EventArgs^ e)
...{
propertyGrid->SelectedObject = Range;
listViewPairs->VirtualListSize=0;
timer->Start();
m_rAmicableNumberPairs->Clear();
backgroundWorker->RunWorkerAsync (Range);
}
在线程创建之后会自动触发DoWork事件。这个事件中的处理类似于1.1中的线程函数体,可以通过访问DoWorkEventArgs参数的argument属性来访问在用RunWorkerAsync启动线程时传递的参数,以及调用ReportProgress定时报告进度。
private: System::Void backgroundWorker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e)
...{
CAmicableNumberRange range=(CAmicableNumberRange)e->Argument;
![]()
int nNumbers=range.Max-range.Min;
int nPercentComplete=0;
![]()
m_rGenerator->Range=range;
m_rGenerator->StartWork();
![]()
while(!backgroundWorker->CancellationPending && m_rGenerator->DoWork())...{
nPercentComplete = (int)(
(float)(m_rGenerator->GetNumberInQuestion()-range.Min)
/(float)nNumbers
* 100);
backgroundWorker->ReportProgress(nPercentComplete,m_rGenerator->GetProgress());
}
m_rGenerator->EndWork();
if(backgroundWorker->CancellationPending)
e->Cancel=true;
}
在主线程中可以处理ProgressChanged事件来更新界面。
private: System::Void backgroundWorker_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e)
...{
progressBar->Value=e->ProgressPercentage;
if(e->UserState!=nullptr)//a new pair is found
...{
CAmicableNumberPair^ anp=(CAmicableNumberPair^)e->UserState;
m_rAmicableNumberPairs->Add(anp);
}
}
在计算完成之后,可以处理RunWorkCompleted事件来更新界面
private: System::Void backgroundWorker_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e)
...{
timer->Stop();
if(e->Error!=nullptr)
MessageBox::Show(e->Error->Message);
progressBar->Value=100;
}
当然,为了避免过于频繁地更新界面,仍然可以使用传统的定时更新方法
private: System::Void timer_Tick(System::Object^ sender, System::EventArgs^ e)
......{
if(IsHandleCreated)
......{
int nOldListSize=listViewPairs->VirtualListSize;
int nNewListSize=m_rAmicableNumberPairs->Count;
if(nNewListSize>nOldListSize)
......{
listViewPairs->VirtualListSize=nNewListSize;
listViewPairs->RedrawItems(nOldListSize, nNewListSize - 1, true);
}
}
}
用户有时会在计算中途取消计算,这时候可以调用CancelAsync方法,然后等待至IsBusy属性成为false为止。在DoWork中检查到CancellationPending属性为true时,应该终止并从DoWork中返回。
void Stop()
...{
backgroundWorker->CancelAsync();
while(backgroundWorker->IsBusy)
...{
Application::DoEvents();
}
}
对于Windows Forms,应该在表单的FormClosing事件处理中自动停止计算,但是对于UserControl,由于没有办法判断其容器的类型,所以只能在容器中手动调用一个方法来终止计算。下面是在MFC8.0的容器类CWinFormsView中调用的方法:
void CAmicableNumbersView::OnDestroy()
...{
m_ViewControl->Stop();
CWinFormsView::OnDestroy();
}
