2017-09-07 33 views
0

我正在與實體框架的WPF應用程序(MVVM)。我想在給定的時間間隔內查詢SQL服務器上的一個表,以向用戶顯示更新的DB值。爲此我成立了一個計時器在我的ViewModel像這樣:System.Timers.Timer.SynchronizingObject ManagedThreadId不是從主線程

RefreshTimer = new System.Timers.Timer(2000.0); 
RefreshTimer.Enabled = true; 
RefreshTimer.SynchronizingObject = new Synchronizer(); 
RefreshTimer.Elapsed += RefreshTimer_Elapsed; 

,其中同步是ISynchronizeInvoke的實現:

internal class Synchronizer : ISynchronizeInvoke 
{ 
    private object _sync = new object(); 

    public bool InvokeRequired { get { return true; } } 

    public IAsyncResult BeginInvoke(Delegate method, object[] args) 
    { 
     var result = new SimpleAsyncResult(); 

     ThreadPool.QueueUserWorkItem(delegate { 
      result.AsyncWaitHandle = new ManualResetEvent(false); 
      try 
      { 
       result.AsyncState = Invoke(method, args); 
      } 
      catch (Exception exception) 
      { 
       result.Exception = exception; 
      } 
      result.IsCompleted = true; 
     }); 

     return result; 
    } 

    public object EndInvoke(IAsyncResult result) 
    { 
     if (!result.IsCompleted) 
     { 
      result.AsyncWaitHandle.WaitOne(); 
     } 

     return result.AsyncState; 
    } 

    public object Invoke(Delegate method, object[] args) 
    { 
     lock (_sync) 
     { 
      return method.DynamicInvoke(args); 
     } 
    } 
} 

internal class SimpleAsyncResult : IAsyncResult 
{ 
    object _state; 

    public bool IsCompleted { get; set; } 

    public WaitHandle AsyncWaitHandle { get; internal set; } 

    public object AsyncState 
    { 
     get 
     { 
      if (Exception != null) 
      { 
       throw Exception; 
      } 
      return _state; 
     } 
     internal set 
     { 
      _state = value; 
     } 
    } 

    public bool CompletedSynchronously { get { return IsCompleted; } } 

    internal Exception Exception { get; set; } 
} 

如果我理解正確的話,則SynchronizingObject的就應該召集所經過的事件SynchronizingObject創建的線程。在我的情況下,這是主線程。現在考慮下面的代碼來處理事件:

private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    System.Diagnostics.Debug.WriteLine($"RefreshTimer ManagedThreadID: {Thread.CurrentThread.ManagedThreadId}"); 
    RefreshTimer.SynchronizingObject.Invoke((Action)(() => { TestMethod(); }), null); 
} 

public void TestMethod() 
{ 
    System.Diagnostics.Debug.WriteLine($"TestMethod ManagedThreadID: {Thread.CurrentThread.ManagedThreadId}"); 
} 

的TestMethod的是我將更新/重裝我的實體對象,以便查看會自動顯示最新的數據。當我嘗試這樣做,我得到一個InvalidOperationException異常,說明,因爲不同的線程擁有它調用線程不能訪問該對象

輸出是:

RefreshTimer ManagedThreadID: 5 
TestMethod ManagedThreadID: 5 
RefreshTimer ManagedThreadID: 4 
TestMethod ManagedThreadID: 4 
RefreshTimer ManagedThreadID: 8 
TestMethod ManagedThreadID: 8 

而且我懷疑這是我得到的理由以上例外。我希望可以將輸出爲:

RefreshTimer ManagedThreadID: 5 
TestMethod ManagedThreadID: 1 
RefreshTimer ManagedThreadID: 4 
TestMethod ManagedThreadID: 1 
RefreshTimer ManagedThreadID: 8 
TestMethod ManagedThreadID: 1 

誰能告訴我,我做錯了什麼,以及如何我可以得到SynchronizingObject.Invoke方法在主線程上執行?

+2

你不應該使用一個工作線程定時器GUI代碼 – MickyD

回答

3

您可以使用調度安排委託進行調度線程上執行:

private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    System.Diagnostics.Debug.WriteLine($"RefreshTimer ManagedThreadID: {Thread.CurrentThread.ManagedThreadId}"); 
    Application.Current.Dispatcher.Invoke(new Action(() => { TestMethod(); })) 
} 

您可能還需要閱讀此:https://www.thomasclaudiushuber.com/2017/02/15/how-to-test-code-that-uses-singletons-like-application-current-dispatcher/