Доступ к потоку пользовательского интерфейса вне контроля - PullRequest
2 голосов
/ 04 июля 2011

У меня есть Session объект, который управляет состояниями объекта (аналогично сеансу nhibernate).Этот сеанс прослушивает события из внешнего источника, которые могут потребовать обновления внутреннего состояния сеанса.Мы попытались реализовать блокировку в сеансе, чтобы гарантировать, что доступ к данным является согласованным, но существует так много непростых крайних случаев.

Вместо этого может быть проще перенести эти события в тот же поток, что и при использовании сеансаэто поток пользовательского интерфейса).Обычно это делается с помощью Control.Invoke(), но поскольку к этому объекту данных нет доступа Control.

Это разумный подход и как я могу получить эти события в потоке пользовательского интерфейса до того, как ониобновить состояние сеанса?Могу ли я использовать Dispatcher и перехватить диспетчер текущего потока при создании сеанса?

Ответы [ 2 ]

1 голос
/ 04 июля 2011

Я думаю, что блокировка обычно довольно проста, и это может быть вашим лучшим вариантом.Реализация того, что вы хотите правильно, вероятно, будет намного сложнее (и, конечно, не без «непростых крайних случаев»).

Что вы могли бы сделать, это использовать существующую реализацию BlockingCollection<T> из.Чистый 4 и очередь из него в один поток.К сожалению, этот поток будет заблокирован, поэтому вы не можете использовать поток пользовательского интерфейса.

1 голос
/ 04 июля 2011

Я бы позволил бизнес-объекту инициировать событие, которое будет перехвачено представлением (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);
        }
    }
...