【发布时间】:2012-01-09 23:14:20
【问题描述】:
我有一个网页抓取程序。在主窗体中,您可以选择一个网站和一个客户端,然后单击 Go,它将启动一个 BackgroundWorker 线程,该线程利用 WebRequest 和 HTMLAgilityPack 来处理对 SiteX 和 ClientX 的一系列请求,每个请求是多个页面。每个 BackgroundWorker 线程都有运行的验证,如果遇到问题,它将弹出一个对话框,让用户中止线程、中止请求、忽略错误或(如果从 IDE 运行)单步执行代码。我希望这个对话框包含一个 WebBrowser 控件,以很好地显示有问题的 HTML 页面。但是,因为它是从 BackgroundWorker 线程调用的,所以我收到异常“当前线程不在单线程单元中”。
这是创建对话框的函数:
protected bool ValidatePage( bool pagePasses, string msg ) {
if ( pagePasses == false ) {
AbortIgnoreSuspend ais = new AbortIgnoreSuspend( responsehtml, msg );
ais.ShowDialog();
switch ( ais.DialogResult ) {
case DialogResult.Abort: // Aborts entire thread
Abort = true;
worker.CancelAsync();
return false;
case DialogResult.Cancel: // Aborts this case
Abort = true;
return false;
case DialogResult.Ignore: // Ignore and continue
return true;
case DialogResult.Retry: // Debug
Debug.Assert( false, "Suspending Thread" );
return true; // Will return you to calling thread and allow you to continue
default:
return true;
}
}
return true;
}
我发现了一些示例,其中将 ApartmentState 设置为 ApartmentState.STA 的 Thread() 将能够创建我的 WebBrowser,因此我进行了以下代码调整:
protected bool ValidatePage( bool pagePasses, string msg ) {
if ( pagePasses == false ) {
bool setAbort = false;
bool assertError = false;
bool cancelWorker = false;
bool returnContinue = true;
Thread th = new Thread( () => {
AbortIgnoreSuspend ais = new AbortIgnoreSuspend( responsehtml, msg );
ais.ShowDialog();
switch ( ais.DialogResult ) {
case DialogResult.Abort: // Aborts entire thread
setAbort = true;
cancelWorker = true;
returnContinue = false;
break;
case DialogResult.Cancel: // Aborts this case
setAbort = true;
returnContinue = false;
break;
case DialogResult.Ignore: // Ignore and continue
returnContinue = true;
break;
case DialogResult.Retry: // Debug
assertError = true;
returnContinue = true; // Will return you to calling thread and allow you to continue
break;
default:
returnContinue = true;
break;
}
} );
th.SetApartmentState( ApartmentState.STA );
th.Start();
th.Join();
Abort = setAbort;
Debug.Assert( !assertError, "Suspending thread for debugging" );
if ( cancelWorker ) { worker.CancelAsync(); }
return returnContinue;
}
return true;
}
这似乎可以工作(我已经使用单个 BackgroundWorker 进行了测试),但是我很确定由于我缺乏使用线程的经验,我在线程安全性方面犯了一些错误。我做错了什么,错过了什么?
【问题讨论】:
-
线程方面没问题。 STA 还必须发送一个消息循环,您可以从 ShowDialog() 免费获得一个。您将遇到的最大问题是对话框有时会消失在用户看不到的另一个窗口后面。或者用户意外关闭对话框,因为她没想到会弹出一个。
标签: c# winforms multithreading browser backgroundworker