2011-11-08 18 views
3

を返し、私は休閑のコードで新しいUIスレッドを作成します。BackgroundWorkerのは、間違ったスレッドに自分のアプリケーションで

Thread thread = new Thread(() => 
    { 
     MyWindow windowInAnotherThread = new MyWindow(); 
     windowInAnotherThread.Show(); 
     System.Windows.Threading.Dispatcher.Run(); 
    }) { IsBackground = true }; 
    thread.SetApartmentState(ApartmentState.STA); 
    thread.Start(); 

これは私の休閑の問題与える:mywindowのクラスのコンストラクタで

を、 BackgroundWorkerが実行されます。 RunWorkerCompletedには、BackgroundWorkerが計算しているいくつかのデータでコントロールを更新する必要があります。

私はこの説明されて小さなサンプル、ビルドする必要があり:(。別のスレッドがそれを所有しているので、呼び出し元のスレッドこのオブジェクトにアクセスすることはできません)私はInvalidOperationExceptionが得るbw_RunWorkerCompleted()

public partial class MyWindow : Window { 
    public MyWindow() { 
     InitializeComponent(); 

     var bw = new BackgroundWorker(); 
     bw.DoWork += bw_DoWork; 
     bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
     bw.RunWorkerAsync(); 
    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
     this.Title = "Calculated title"; 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) { 
     Thread.Sleep(3000); 
    } 
} 

を。それは、BackgroundWorkerが開始された正しいUIスレッドに戻っていないようです。

私はこの問題を解決するために何ができるのですか? BackgroundWorkerを実行しているコードを変更することはできません。なぜなら、それは私が使用しているフレームワークにあるからです。しかし、私はRunWorkerCompleted-Eventで何か他のことをすることができます。しかし、私はこの問題を解決する方法を知らない。

+0

なぜ新しいUIスレッドを作成するのですか?必要ではなく、生産性とこの問題の原因ではありません。 –

答えて

4

問題はウィンドウがあまりにも早く作成なっているということです。スレッドにはまだ同期コンテキストがありません。 BGWコンストラクタ呼び出しにブレークポイントを設定し、Thread.CurrentThread.ExecutionContext.SynchronizationContextを見て、これがデバッガであることがわかります。それはnullです。これは、BGWがRunWorkerCompletedイベントをマーシャリングする方法を決定するために使用するものです。どの同期化コンテキストもなく、イベントはスレッドプールスレッド上で実行され、怒りを引き起こす。

ディスパッチャを早期に初期化する必要があります。 100%ではありませんが、これは正しい方法ですが、正常に動作したようです:

 Thread thread = new Thread(() => { 
      System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { 
       MyWindow windowInAnotherThread = new MyWindow(); 
       windowInAnotherThread.Show(); 
      })); 
      System.Windows.Threading.Dispatcher.Run(); 
     }) { IsBackground = true }; 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.Start(); 

また、スレッドを強制的にシャットダウンする必要があります。 mywindowのために、このメソッドを追加します。

protected override void OnClosed(EventArgs e) { 
     Dispatcher.BeginInvokeShutdown(System.Windows.Threading.DispatcherPriority.Background); 
    } 
+0

またはbgwの作成が遅れていると思います。 –

+0

BeginInvokeをこのように呼び出すことはできません。これはウィンドウの作成をUIスレッドにプッシュするためですDispatcher.BeginInvokeは現在のウィンドウのディスパッチャであり、新しいスレッドではありません...) –

+0

'Dispatcher.CurrentDispatcher.BeginInvoke(...)'を使用した場合、これはうまくいくと思います。 –

0

MyWindow中にあなたBackgroundWorkerためgettersetterを提供してください。また、Setterメソッドを介してBackgroundWorkerオブジェクトをMywindowに渡します。それは問題を解決するはずです。

0

呼び出し関数でデリゲートメソッドと呼び出しを使用する必要があります。良い例がここにあります:あなたのコードを使用してhttp://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx

public partial class MyWindow : Window { 


    delegate void TitleSetter(string title); 

    public MyWindow() { 
      InitializeComponent(); 

     var bw = new BackgroundWorker(); 
     bw.DoWork += bw_DoWork; 
     bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
     bw.RunWorkerAsync(); 
    } 

    void SetTitle(string T) 
    { 
     this.Title = T; 
    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 

     try  
     { 
     TitleSetter T = new TitleSetter(SetTitle); 
     invoke(T, new object[]{"Whatever the title should be"}); //This can fail horribly, need the try/catch logic. 
     }catch (Exception){} 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) { 
     Thread.Sleep(3000); 
    } 
} 
+0

これは必要ではありません。そしてWPFでは 'Dispatcher.Invoke()'です –

0

私は単にあなたのバックグラウンドワーカースレッドのセットアップ「ロード」イベントにコードの代わりに、コンストラクタを移動するだけで罰金されるべきだと思います。

1

問題は、SynchronizationContextを設定する必要があることです。 Dispatcher.Invokeはあなたのために設定しますが、コンストラクタでBackgroundWorker(これはDispatcher.Runより前に実行されます)を使用しているため、コンテキストは設定されません。これは通常問題ではありません。

にあなたのスレッドの作成を変更

SynchronizationContextが所定の位置にウィンドウの建設前にであるように、これは、それが正しく実行するようになります

Thread thread = new Thread(() => 
    { 
     // Create the current dispatcher (done via CurrentDispatcher) 
     var dispatcher = Dispatcher.CurrentDispatcher; 
     // Set the context 
     SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(dispatcher)); 

     MyWindow windowInAnotherThread = new MyWindow(); 
     windowInAnotherThread.Show(); 
     Dispatcher.Run(); 
    }); 

thread.SetApartmentState(ApartmentState.STA); 
thread.IsBackground = true; 
thread.Start(); 

2

同様の問題があります。下の注1と2に基づいて、私はUIBackgroundWorkerを作成しました。この問題に遭遇した他の開発者を助けることができるかもしれません。

それが動作する場合は、他の開発者の利益のためにデザインを知らせてください。

public class UIBackgroundWorker : BackgroundWorker 
{ 

    private System.Windows.Threading.Dispatcher uiDispatcher; 
    public SafeUIBackgroundWorker(System.Windows.Threading.Dispatcher uiDispatcher) 
     : base() 
    { 
     if (uiDispatcher == null) 
      throw new Exception("System.Windows.Threading.Dispatcher instance required while creating UIBackgroundWorker"); 
     else 
      this.uiDispatcher = uiDispatcher; 
    } 

    protected override void OnProgressChanged(ProgressChangedEventArgs e) 
    { 
     if (uiDispatcher.CheckAccess()) 
      base.OnProgressChanged(e); 
     else 
      uiDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => base.OnProgressChanged(e))); 
    } 

    protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e) 
    { 
     if (uiDispatcher.CheckAccess()) 
      base.OnRunWorkerCompleted(e); 
     else 
      uiDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => base.OnRunWorkerCompleted(e))); 
    } 
} 
関連する問題