定时器和线程有问题

我是一个不熟练的C#编码器,这就是为什么无论互联网上的信息量如何,这个问题都让我感到非常困难。

我实际上是在一个计时器上创建一个程序,它反复轮询网站以获取一些信息。 在此过程中,将创建WebBrowser控件以导航到信息(身份validation所需)。 该程序在启动时运行这一系列事件,然后使用System.Timers.Timer设置为每10分钟(当然调试较少)来执行相同系列的事件,但当我的Timer.Elapsed事件触发该过程时,我得到A:

无法实例化描述为ActiveX控件’8856f961-340a-11d0-a96b-00c04fd705a2’的 ThreadStateException因为当前线程不在单线程单元中。

这是我的程序的精简版。

private void Form1_Load(object sender, EventArgs e) { GetDataFromWebBrowser(); Set_Auto_Refresh_Timer(); } private void Set_Auto_Refresh_Timer() { System.Timers.Timer TimerRefresh = new System.Timers.Timer(10000); TimerRefresh.Elapsed += new System.Timers.ElapsedEventHandler(TimerRefresh_Elapsed); TimerRefresh.AutoReset = true; TimerRefresh.Start(); } private void TimerRefresh_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { GetDataFromWebBrowser(); } private void GetDataFromWebBrowser() { WebBrowser wb = new WebBrowser(); <--This is where the error is thrown. ...get web data... } 

我想我在那里有足够的代码来描绘图片。 正如您所看到的,当它创建另一个WebBrowser时,它会抛出错误。

我真的很难过,我只是开始在线程上刮起表面,这可能就是我为什么这么难过的原因。

//我/我的解决方案最终将WebBrowser创建移出方法,并使其静态以重用WebBrowser控件。 我还将System.Timers.Timer交换为System.Threading.Timer。 似乎解决了这个问题。

WebBrowser的MSDN文档指出:

WebBrowser类只能在设置为单线程单元(STA)模式的线程中使用。 要使用此类,请确保使用[STAThread]属性标记 Main方法。

此外,如果要以固定间隔与UI控件进行交互,请将System.Timers.Timer更改为System.Windows.Forms.Timer 。 或者,将System.Timers.Timer的SynchronizingObject属性设置为父控件,以强制计时器调用右侧线程上的调用。 所有WinForms控件只能从同一个,唯一的UI线程访问。

.NET的BCL中有三种类型的定时器,每种定时器的行为都非常不同。 请查看此MSDN文章以进行比较: 比较.NET Framework类库(Web存档)中的Timer类或此简要比较表 。

我建议使用WebClient类而不是WebBrowser。 此外,将已创建的实例存储为私有属性似乎更好,而不是每次需要轮询网站时创建新实例。

如下:

 private WebClient webClient; private void Form1_Load(object sender, EventArgs e) { GetDataFromWebBrowser(); Set_Auto_Refresh_Timer(); this.webClient = new WebClient(); } private void Set_Auto_Refresh_Timer() { System.Timers.Timer.TimerRefresh = new System.Timers.Timer(10000); TimerRefresh.Elapsed += new System.Timers.ElapsedEventHandler(TimerRefresh_Elapsed); TimerRefresh.AutoReset = true; TimerRefresh.Start(); } private void Set_Auto_Refresh_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { GetDataFromWebBrowser(); } private void GetDataFromWebBrowser() { ...perform required work with webClient... ...get web data... } 

正如Groo所说,你应该使用System.Windows.Forms.Timer ,或者如果你真的想在另一个线程中进行操作,你应该使用Invoke方法来做任何与UI相关的事情:

 private void GetWebData() { ...get web data... } private void ShowWebData() { WebBrowser wb = new WebBrowser(); // other UI stuff } private void TimerRefresh_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { GetDataFromWebBrowser(); } private void GetDataFromWebBrowser() { GetWebData(); if (this.InvokeRequired) this.Invoke(new Action(ShowWebData)); else ShowWebData(); }