UI 线程访问不受控制

发布于 2024-11-18 06:03:18 字数 349 浏览 3 评论 0原文

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

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

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

I have a Session object that manages object states (similar to a nhibernate session). This session listens to events from an external source that may require updates to the internal session state. We've tried implementing locking in the session to ensure the data accessed is coherent but there's so many fiddly edge cases.

Instead it might be easier to marshal these events onto the same thread as is using the session (which is the UI thread). Generally this is done with Control.Invoke() but because this is a data object there is no Control to access.

Is this a reasonable approach and how can I go about getting this events onto the UI thread before they update the session state? Can I use the Dispatcher and capture the current thread's dispatcher when the session is created?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

給妳壹絲溫柔 2024-11-25 06:03:18

我会让业务对象触发一个由视图 ( 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);
        }
    }

I would let the business object fire an event to be caught by the view ( UI ) and do the marshaling on that event handler, so you have in that place a Control to understand if an invoke is needed or not:

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;
        }

    }

if you are using WPF, you can take inspiration from 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);
        }
    }
我是男神闪亮亮 2024-11-25 06:03:18

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

您可以做的是使用 BlockingCollection并在单线程中从中出列。不幸的是,该线程将被阻塞,因此您无法使用 UI 线程。

I think that locking is usually pretty straight-forward and it may be your best option. Implementing what you want correctly would probably be much more difficult (and certainly not without “fiddly edge cases”).

What you could do though is use existing implementation of BlockingCollection<T> from .Net 4 and dequeue from it in single thread. Unfortunately, that thread would be blocked, so you can't use the UI thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文