2011-07-04 70 views
2

我有一个Session对象管理对象状态(类似于一个nhibernate会话)。此会话侦听来自外部源的事件,这些事件可能需要更新到内部会话状态。我们已经尝试在会话中实现锁定,以确保访问的数据是一致的,但存在这么多棘手的边缘案例。UI线程访问外部控制

相反,使用会话(这是UI线程)将这些事件编组到相同的线程可能更容易。通常这是用Control.Invoke()完成的,但是因为这是一个数据对象,所以没有Control来访问。

这是一种合理的方法,我怎样才能在更新会话状态之前将这些事件引入UI线程?当会话创建时,我可以使用Dispatcher并捕获当前线程的调度程序吗?

+1

'Dispatcher'在WPF中使用,而ControlForvo()'在WinForms中,你使用哪一个? – svick

+0

@svick - WinForms –

回答

1

我会让业务对象触发一个事件被视图(UI)捕获并对该事件处理程序进行封送处理,因此您在该位置有一个Control以了解是否需要调用:

public static class ControlExtentions 
    { 
     public delegate void InvokeHandler(); 
     public static bool SafeInvoke(this Control control, InvokeHandler handler) 
     { 
      if (control.InvokeRequired) 
      { 
       try 
       { 
        control.Invoke(handler); 
       } 
       finally { } 
       return false; 
      } 
      else 
       handler.Invoke(); 
      return true; 
     } 

    } 

如果你使用WPF,你可以从CaliburnMicro灵感:

public static class Execute 
    { 
     private static Action<System.Action> executor = action => action(); 

     /// <summary> 
     /// Initializes the framework using the current dispatcher. 
     /// </summary> 
     public static void InitializeWithDispatcher() 
     { 
#if SILVERLIGHT 
      var dispatcher = Deployment.Current.Dispatcher; 

      executor = action => { 
       if(dispatcher.CheckAccess()) 
        action(); 
       else { 
        var waitHandle = new ManualResetEvent(false); 
        Exception exception = null; 
        dispatcher.BeginInvoke(() => { 
         try { 
          action(); 
         } 
         catch(Exception ex) { 
          exception = ex; 
         } 
         waitHandle.Set(); 
        }); 
        waitHandle.WaitOne(); 
        if(exception != null) 
         throw new TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception); 
       } 
      }; 
#else 
      var dispatcher = Dispatcher.CurrentDispatcher; 

      executor = action => { 
       if(dispatcher.CheckAccess()) 
        action(); 
       else dispatcher.Invoke(action); 
      }; 
#endif 

     } 

     /// <summary> 
     /// Resets the executor to use a non-dispatcher-based action executor. 
     /// </summary> 
     public static void ResetWithoutDispatcher() { 
      executor = action => action(); 
     } 

     /// <summary> 
     /// Executes the action on the UI thread. 
     /// </summary> 
     /// <param name="action">The action to execute.</param> 
     public static void OnUIThread(this System.Action action) { 
      executor(action); 
     } 
    } 
1

我认为锁定通常是相当直接的,它可能是你最好的选择。正确实施你想要的东西可能会困难得多(当然也不是没有“肮脏的边缘情况”)。

你可以做的是从.Net 4使用现有的BlockingCollection<T>实现,并在单线程中将它从队列中取出。不幸的是,该线程会被阻塞,所以你不能使用UI线程。