2011-10-18 24 views
0

考慮下面的代碼爲WPF ViewModel的執行:獲得一個的TaskScheduler /的SynchronizationContext對特定線程

protected void Init() 
{ 
    Debug.WriteLine(string.Format("ChangeManager init on thread={0}", Thread.CurrentThread.ManagedThreadId));   

    var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

    this.modelChanged = (o, args) => Task.Factory.StartNew(() => 
     {     
      Debug.WriteLine(string.Format("ModelChanged on thread={0}", Thread.CurrentThread.ManagedThreadId)); 
      this.ModelChanged(o, args); 
     }, 
     CancellationToken.None, 
     TaskCreationOptions.None, 
     uiTaskScheduler); 
} 

...其中modelChanged是應對變化中的一個對象模型的事件處理程序。此代碼在UI線程上執行,旨在讓UI線程處理事件,而不管它們是從哪個線程觸發的。

然而,當這個被運行的輸出類似於:

ChangeManager init on thread=1 
ModelChanged on thread=3 
ModelChanged on thread=3 
ModelChanged on thread=7 
ModelChanged on thread=9 

我的預期是線程1將所有的處理會發生。即使當我嘗試像這樣直接使用SynchronizationContext:

protected void Init() 
{ 
    Debug.WriteLine(string.Format("ChangeManager init on thread={0}", Thread.CurrentThread.ManagedThreadId));   

    this.uiContext = SynchronizationContext.Current;   

    modelChanged = (o, args) => uiContext.Post((ignore) => { 
     Debug.WriteLine(string.Format("ModelChanged on thread={0}", Thread.CurrentThread.ManagedThreadId)); 
     this.ModelChanged(o, args); 
    } 
    , null); 
} 

...我看到同樣的事情。

我的想法或方法有什麼問題嗎?我如何獲得要在init線程上處理的事件?

在此先感謝!

回答

2

有趣的是,你的代碼適合我。也許你遺漏了可以解釋問題的部分代碼。你可以發佈一個更完整的問題再現嗎?具體來說,除了分配lambda表達式之外,還要顯示您正在使用modelChanged成員所做的事情。

我所做的是,創建一個空的WPF應用程序,並從主窗口的構造函數運行您的Init方法。

然後我開始後臺線程直接調用modelChanged委託。

我看到的是「ModelChanged on thread ...」這一行總是打印出正確的線程,這個線程叫做Init

如果它的任何幫助,這是我沒有嘗試重現它,你可以看看它,也許張貼關於你在做什麼不同:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     Init(); 
    } 

    private EventHandler modelChanged; 

    protected void Init() 
    { 
     Trace.WriteLine(string.Format("ChangeManager init on thread={0}", 
       Thread.CurrentThread.ManagedThreadId)); 

     var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

     modelChanged = (o, args) => Task.Factory.StartNew(() => 
     { 
      Trace.WriteLine(string.Format("ModelChanged on thread={0}", 
       Thread.CurrentThread.ManagedThreadId)); 

      if (ModelChanged != null) 
      { 
       ModelChanged(o, args); 
      } 
     }, 
      CancellationToken.None, 
      TaskCreationOptions.None, 
      uiTaskScheduler); 
    } 

    public event EventHandler ModelChanged; 

    private void button1_Click(object sender, RoutedEventArgs e) 
    { 
     var t = new Thread(
      obj => 
       { 
         Trace.WriteLine(string.Format(
          "Launching handler on thread={0}", 
          Thread.CurrentThread.ManagedThreadId)); 

         modelChanged(null, EventArgs.Empty); 
       }); 
     t.Start(); 
    } 
} 
0

看起來像你的情況Init() ISN」運行在UI線程上。爲了確保東西在UI線程上運行,你可以使用一些控件(例如,Window的)Dispatcher財產,並用它來在UI線程上像這樣運行代碼:

someControl.Dispatcher.Invoke(() => { /* do something with the UI */ }); 

Invoke()這種特殊的過載需要參考System.Windows.Presentation.dllusing System.Windows.Threading;指令的擴展方法,並且需要.NET 3.5 SP1及更高版本。

相關問題