2012-01-09 11 views
1

私はWebスクレイピングプログラムを持っています。メインフォームから、Webサイトとクライアントを選択して[Go]をクリックすると、WebRequestとHTMLAgilityPackを使用してSiteXとClientXに対する一連の要求を処理するBackgroundWorkerスレッドが開始され、各要求は複数のページになります。各BackgroundWorkerスレッドは実行されているバリデーションを持っており、問題が発生した場合、スレッドを中止したり、要求を中止したり、エラーを無視したり、(IDEから実行した場合は)コードにステップするダイアログボックスを表示します。このダイアログボックスに問題のHTMLページをうまくレンダリングして表示するWebBrowserコントロールが必要です。しかし、BackgroundWorkerスレッドから呼び出されているため、「現在のスレッドはシングルスレッドアパートメントにはありません」という例外が発生します。 ApartmentStateは私のWebBrowserコントロールを作成することができるだろうApartmentState.STAに設定するので、私は次のように行われBackgroundWorkerからWebBrowserでダイアログを作成する

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; 
} 

スレッドを開始するところ私は例を発見した()で:ここで

がダイアログを作成する関数ですコードの調整は:

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でテストしている)動作しているように見えます。私が間違ったことをしたこと、私が逃したことは何ですか?

+1

スレッドには問題ありません。 STAはまた、メッセージループをポンピングする必要があります。あなたはShowDialog()から無料で1つを取得します。あなたが持っている最大の問題は、ダイアログが別のウィンドウの後ろに消えることがあり、ユーザには見えないことです。また、ユーザーがポップアップすることを期待していなかったために、ユーザーが誤ってダイアログを閉じた。 –

答えて

0

あなたは.NETでスレッドを作成する方法に応じて、.NETはMTAまたはSTAと呼ばれる獣にそのスレッドのアパートを設定します。 MTAとSTAはどちらもCOMテクノロジです。それ以外では、.NETでほとんど使用されていません(MSの.NETドキュメントに記載されています)。 MSは、明示的に設定しないと、特定の.NETスレッドをMTAにデフォルト設定するのが好きです。スレッドが主なUI STAスレッドではないと思われます。

.NETでかなり気の利いたBackgroundWorkerを使用しています。 WorkerReportsProgressに設定した場合、ProgressChangedのハンドラを設定し、この新しいハンドラ内で呼び出されるダイアログコードを移動して問題を解決してください。

ProgressChangedは実際に進捗状況を報告する必要はありません。特に、スレッドコンテキストスイッチが必要な場合は、何かのために使用できます。 ReportProgressに渡す引数には、好きなオブジェクトを指定することもできます。

これはなぜ機能しますか? ProgressChangedは、ワーカースレッドのコンテキストからプライマリユーザーインターフェイス(UI)スレッドのスレッドマーシャリングに自動的にスレッドマーシャリングを実行します。 UIスレッドでのみ、UI呼び出しを安全に実行できます。

関連する問題