2012-01-31 58 views
4

我是一個不是很先進的學習示例C#編碼器,這就是爲什麼無論互聯網上的信息量如何,這個問題完全困擾我。定時器和線程的問題

我本質上是創建一個程序,在計時器上反覆輪詢網站以獲取一些信息。在此過程中,將創建一個WebBrowser控件以導航到信息(需要進行身份驗證)。程序在啓動時運行這一系列事件,然後使用一個設置爲每10分鐘(當然,調試時減少)的System.Timers.Timer來做同樣系列的事件,但是當我的Timer.Elapsed事件觸發該過程時,我得到一個:

ThreadStateException與說明書ActiveX控件「{8856F961-340A-11D0-A96B-00c04fd705a2」不能被實例化,因爲當前線程不是在一個單一的線程單元。

這是我的程序的瘦身版本。

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。似乎解決了這個問題。

+1

這是因爲WebBrowser控件是不允許在自己的線程。你可以使用WebClient嗎?否則有一個[STAThread]屬性的方法..不知道這是否工作.. – f2lollpll 2012-01-31 07:49:10

+1

我不知道WebBrowser控件,但通常控件只能在窗體運行的線程中使用。您可以切換到一個系統。 Windows.Forms.Timer(在這種情況下你不會是多線程的),或者更難以使用WebRequest(需要一些工作才能獲得授權) – 2012-01-31 07:49:26

回答

7

MSDN documentation for WebBrowser指出:

web瀏覽器類只能在設置爲單線程單元(STA)模式的線程中使用。要使用此類,請確保您的Main方法是,標記爲[STAThread]屬性

此外,如果要定期與UI控件進行交互,請將System.Timers.Timer更改爲System.Windows.Forms.Timer。或者,將System.Timers.TimerSynchronizingObject屬性設置爲父控件,以強制您的計時器在右側線程上調用調用。所有的WinForms控件只能從相同的,唯一的UI線程訪問。

.NET的BCL中有三種類型的定時器,它們每一種的表現都非常不同。查看此MSDN文章以進行比較:Comparing the Timer Classes in the .NET Framework Class Library (web archive)或此brief comparison table

+0

我試圖將auto_refresh計時器交換到Windows.Form。計時器,但它不會觸發.Tick事件,我不知道爲什麼。文檔沒有給我任何提示。 – Josh 2012-01-31 20:19:59

+0

@Josh:這很奇怪。你確定你開始了嗎?並且'Interval'值是合理的?你能發佈你的代碼嗎? – Groo 2012-01-31 22:40:29

+0

[STAThread]屬性實際上並沒有工作,但正如你所建議的那樣,我交換了一個不同的定時器,這有助於 - 以及一些其他的代碼修改。間隔值爲30秒左右用於測試。我最終與Threading.Timer一起工作,這對我的意大利麪條代碼來說工作得很好。 – Josh 2012-02-02 08:12:48

3

我會推薦使用WebClient類而不是WebBrowser。此外,將已創建的實例作爲私有屬性存儲似乎更好,而不是每次需要輪詢Web站點時都創建新實例。

如下:

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... 

     } 
+0

+1是的,實際上這是正確的方式。創建一個不會與用戶交互的控件是錯誤的。 – Groo 2012-01-31 08:01:09

+0

選擇使用WebBrowser是因爲我花了大量時間試圖讓WebClient工作,但無法使認證過程成功。 WebClient是我的第一選擇。 – Josh 2012-01-31 08:08:28

+0

對於身份驗證,您可以傳遞[NetworkCredential](http://msdn.microsoft.com/en-us/library/system.net.networkcredential.aspx)類的實例。對於我來說,指定登錄名,密碼和域名就足夠了,如下面的'新的NetworkCredential(「userId」,「password」,「domainName」);'。 – 2012-01-31 08:23:35

0

正如Groo說,你應該使用System.Windows.Forms的。定時器,或者如果你真的想這樣做你運行在另一個線程,您應該使用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(); 
}