2017-09-07 37 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/