Лучшие практики для перемещения объектов в отдельный поток - PullRequest
0 голосов
/ 03 сентября 2010

У нас есть реализация для текущего приложения ультразвуковой машины, где объект Ultrasound создается в потоке пользовательского интерфейса. Реализация Singleton здесь была бы хороша, но не так.

В последнее время установленные методы изменились так, что они автоматически останавливают и перезапускают ультразвуковой аппарат, что может занять от 10 до 100 мс в зависимости от состояния аппарата. В большинстве случаев это неплохая проблема, однако она все еще вызывает блокировку потока пользовательского интерфейса на 100 мс. Кроме того, эти методы не являются поточно-ориентированными и должны вызываться в том же потоке, где был инициализирован объект.

Эта самая большая проблема, которую сейчас вызывает, - это не отвечающие кнопки в пользовательском интерфейсе, особенно ползунки, которые могут пытаться обновлять переменные много раз при перемещении панели. В результате ползунки особенно будут заикаться и обновляться очень медленно, так как они совершают множество заданных вызовов через свойства с привязкой к данным.

Каков хороший способ создать поток специально для создания и работы с этим ультразвуковым объектом, который будет сохраняться в течение всего жизненного цикла приложения?

Текущий временный обходной путь включает в себя порождение таймера и вызов обновления параметра, как только мы обнаружим, что ползунок не перемещается в течение 200 мс, однако тогда для каждого ползунка должен быть реализован таймер, и это выглядит как очень грязное решение, которое решает невосприимчивые ползунки, но все еще иногда блокирует поток пользовательского интерфейса.

Ответы [ 3 ]

4 голосов
/ 03 сентября 2010

Одна вещь, которая действительно хороша в программировании GUI, это то, что вам не нужно беспокоиться о том, что несколько потоков могут испортить вам жизнь (при условии, что у вас есть CheckForIllegalCrossThreadCalls = true, как и должно быть). Это все однопоточные, работающие с помощью насоса сообщений (очереди), который обрабатывает входящие сообщения по одному.

Поскольку вы указали, что вам нужно синхронизировать вызовы методов, которые не написаны как поточно-ориентированные (полностью понятные), нет никаких причин, по которым вы не можете реализовать свой собственный обработчик сообщений для обработки ваш ультразвуковой объект.

Наивная, очень упрощенная версия может выглядеть примерно так (класс BlockingCollection<T> отлично подходит, если вы используете .NET 4.0 или установили Rx-расширения ; в противном случае, Вы можете просто использовать простую ваниль Queue<T> и сделать свою собственную блокировку). Предупреждение : это просто быстрый скелет, который я сейчас собрал; Я не даю никаких обещаний относительно его прочности или даже правильности.

class MessagePump<T>
{
    // In your case you would set this to your Ultrasound object.
    // You could just as easily design this class to be "object-agnostic";
    // but I think that coupling an instance to a specific object makes it clearer
    // what the purpose of the MessagePump<T> is.
    private T _obj;

    private BlockingCollection<Action<T>> _workItems;
    private Thread _thread;

    public MessagePump(T obj)
    {
        _obj = obj;

        // Note: the default underlying data store for a BlockingCollection<T>
        // is a FIFO ConcurrentQueue<T>, which is what we want.
        _workItems = new BlockingCollection<Action<T>>();

        _thread = new Thread(ProcessQueue);
        _thread.IsBackground = true;
        _thread.Start();
    }

    public void Submit(Action<T> workItem)
    {
        _workItems.Add(workItem);
    }

    private void ProcessQueue()
    {
        for (;;)
        {
            Action<T> workItem = _workItems.Take();
            try
            {
                workItem(_obj);
            }
            catch
            {
                // Put in some exception handling mechanism so that
                // this thread is always running. One idea would be to
                // raise an event containing the Exception object on a
                // threadpool thread. You definitely don't want to raise
                // the event from THIS thread, though, since then you
                // could hit ANOTHER exception, which would defeat the
                // purpose of this catch block.
            }
        }
    }
}

Тогда произойдет следующее: каждый раз, когда вы хотите каким-либо образом взаимодействовать с вашим ультразвуковым объектом, вы делаете это через этот насос сообщений, вызывая Submit и выполняя какое-то действие, которое работает с вашим ультразвуковым объектом. Затем объект ультразвука получает все сообщения, отправленные ему синхронно (я имею в виду, по одному за раз), работая в своем собственном потоке без GUI.

3 голосов
/ 03 сентября 2010

Вы должны поддерживать выделенный поток UltraSound, который создает объект UltraSound и затем прослушивает обратные вызовы от других потоков.

Вы должны поддерживать потокобезопасную очередь делегатов, чтобы поток UltraSound неоднократно выполнялся и удалял первого делегата в очереди.

Таким образом, поток пользовательского интерфейса может публиковать действия в очереди, которые затем будут выполняться асинхронно потоком UltraSound.

0 голосов
/ 03 сентября 2010

Я не уверен, что полностью понимаю настройки, но вот моя попытка решения:

Как насчет того, чтобы обработчик событий для ползунка проверил время последнего события и подождал 50 мс, прежде чем обрабатывать настройку пользователя (обрабатывать только самое последнее значение).

Затем создайте поток, используя цикл while и ожидающий триггера AutoResetEvent из GUI. Затем он создаст объект и установит его?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...