2

我有一个Session管理对象状态的对象(类似于休眠会话)。此会话侦听来自外部源的事件,这些事件可能需要更新内部会话状态。我们已经尝试在会话中实现锁定以确保访问的数据是连贯的,但是有很多复杂的边缘情况。

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

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

4

2 回答 2

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);
        }
    }
于 2011-07-04T09:16:01.127 回答
1

我认为锁定通常非常简单,它可能是您的最佳选择。正确实现你想要的可能会更加困难(当然也不是没有“繁琐的边缘情况”)。

不过,您可以做的是使用BlockingCollection<T>.Net 4 的现有实现并在单线程中将其出列。不幸的是,该线程将被阻塞,因此您无法使用 UI 线程。

于 2011-07-04T09:47:01.413 回答