2011-11-08 2 views
3

에 반환, 나는 fallowing 코드와 함께 새로운 UI 스레드를 생성 BackgroundWorker가 실행됩니다. RunWorkerCompleted에는 BackgroundWorker가 계산하는 일부 데이터로 컨트롤을 업데이트해야합니다.BackgroundWorker에 내 응용 프로그램에서 잘못된 스레드

나는이를 설명하는 작은 샘플 구축이 있습니다. (다른 스레드가 그것을 소유하고 있기 때문에이 개체에 액세스 할 수 없습니다 호출 스레드) 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); 
    } 
} 

은 내가 InvalidOperationException를 얻을 수 있습니다. 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

BackgroundWorkerMyWindowgettersetter을 입력 해보십시오. 그리고 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이 설정하기 때문에 문제가되지 않지만, Dispatcher.Run보다 먼저 생성자에서 BackgroundWorker을 사용하고 있으므로 컨텍스트가 설정되지 않습니다.이것은 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))); 
    } 
} 
관련 문제