我有一个网络抓取程序。从主窗体中,您可以选择一个网站和一个客户端,然后单击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;
}
这个APPEARS可以工作(我已经使用单个BackgroundWorker进行了测试),但我很确定由于我缺乏使用Threads的经验,我在线程安全方面犯了一些错误。我做错了什么,我错过了什么?
答案 0 :(得分:0)
根据您在.NET中创建线程的方式,.NET会将该线程的公寓设置为名为 MTA 或 STA 的野兽。 MTA和STA都是COM技术;除此之外,它们在.NET中几乎没有用(如MS的.NET文档中所述)。如果您没有明确设置它,MS喜欢将某些.NET线程默认为MTA,我怀疑您的线程不是主要的UI STA线程。
我看到你在.NET中使用了相当漂亮的BackgroundWorker。如果将 WorkerReportsProgress 设置为 true ,请为 ProgressChanged 设置处理程序,并移动要在此新处理程序中调用的对话框代码,以解决您的问题。
ProgressChanged实际上不必实际报告进度,您可以将它用于任何事情,特别是当您需要线程上下文切换时。传递给ReportProgress的参数也可以是您喜欢的任何对象。
为什么这样做? ProgressChanged自动从工作线程的上下文到主用户界面(UI)线程的上下文执行线程编组。只有在UI线程中才能安全地执行UI调用。