Шаблон проектирования, Qt Model / View и несколько потоков - PullRequest
12 голосов
/ 28 февраля 2012

Я создаю приложение, которое отображает рыночные данные и использует их также в некоторых других формах.Я храню рыночные данные на карте, скажем std::map<tickerId, StockData>.Позвольте мне привести один использованный случай использования этой карты.

  1. сеть отправляет пакет данных, инкапсулирующий базовые данные в момент времени t.updatePrice(tickerId, latestPrice)
  2. обновить данные о запасах на карте.Теперь несколько потоков могут получить доступ / обновить данные.Таким образом, карта должна быть заблокирована для поточно-ориентированных операций.Вот первый вопрос, нужно ли мне также блокировать базовые данные для обновлений?
  3. Существует многократное использование новых данных о запасах, скажем, есть обновление цены на IBM, затем мне нужно обновитьстоимость IBM в моем портфолио.А также отображать новые данные на экране.И может быть несколько других одновременных применений. updatePosition(tickerId, price) и updateStockScreen(tickerId, price).Кроме того, отделение обновлений Gui от обновления позиции важно, так как GUI не является основной сильной стороной приложения.
  4. Я просто обеспокоен тем, как реализовать этот тип дизайна.Я читал о модели / представлении дизайна в QT для отображения данных, но если поток представления читает с той же карты, он должен быть заблокирован.Это приводит к медленному / неэффективному дизайну.Каждый раз, когда представление считывает модель, модель должна быть заблокирована.Это предпочтительно в графическом интерфейсе в реальном времени?
  5. Подводя итог, я сохранил много различных объектов в виде карт.И объекты обновляются в реальном времени.Мне нужно обновить их, а затем использовать их в разных местах.Было бы здорово, если бы кто-нибудь дал мне небольшой пример того, как реализовать такие проекты.

Некоторые ссылки на полезные книги также приветствуются.

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

Спасибо, Шив

Ответы [ 2 ]

11 голосов
/ 29 февраля 2012

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

Если это так ... и ваша модель доступна только для чтения через виджет, тогда да, вам нужно заблокировать. Я бы сказал, что это подрывает элегантность «разъединения», обеспечиваемого разделением модель / вид. Но это можно заставить работать.

Однако ... если ваша модель предназначена для чтения и записи в представлении, невозможно выполнить корректно на всех из-за очереди в слотах для уведомлений. Вот архив разговора в списке рассылки, который я имел в списке рассылки qt-Interest по теме:

http://blog.hostilefork.com/qt-model-view-different-threads/

"Короче говоря, я не думаю, что для Модели возможно
быть измененным в потоке без графического интерфейса ... независимо от того, является ли модель
данные были защищены с помощью блокировки чтения / записи. Если что я собираю
верно, тогда Qt, вероятно, должен утверждать, что модель и
его взгляды имеют ту же привязку к потоку (похоже, сейчас этого не происходит) "

Последующий модульный тест разработчиком KDE подтвердил это.

Мне кажется, что лучший способ обойти это - сохранить модель и представление в одном потоке и изменять только модель в потоке GUI. Поэтому, если рабочий поток хочет изменить его, он должен использовать сигнал.

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

1 голос
/ 05 июня 2017

Сегодня я узнал еще одну потенциальную проблему, трудный путь, даже если модель была только для чтения. Я использую другой поток для изменения данных в модели (фактически, моя программа состоит из более чем 20 потоков, и все они играют хорошо), который затем обновляет таймер Qt. Это работает очень хорошо, но есть проблема, с которой я столкнулся:

Вы не можете заблокировать между rowCount / columnCount и data().

Qt работает последовательно, что означает, что на человеческом языке он будет спрашивать «насколько вы велики», а затем спрашивать «какие данные у вас есть на этой позиции», и они могут сломаться.

Рассмотрим:

int FilesQueue::rowCount(const QModelIndex &/*parent*/) const
{
    std::lock_guard<decltype(mainQueueMutex)> lg(mainQueueMutex);
    return filesQueue.size();
}
QVariant FilesQueue::data(const QModelIndex &index, int role) const
{
    std::lock_guard<decltype(mainQueueMutex)> lg(mainQueueMutex);
    if ( role == Qt::DisplayRole) {
        return filesQueue[index.row()]->getFilename();
    }
}

Qt будет делать звонки так:

//...
obj->rowCount();
obj->data(...);
//...

И повсеместно произошел сбой утверждения, потому что просто между rowCount() и data() был поток, который изменял размер данных! Это сломало программу. Итак, это происходило:

//...
obj->rowCount();
//another thread: filesQueue.erase(...)
obj->data(...);
//...

Мое решение проблемы - снова проверить размер в методе data ():

QVariant FilesQueue::data(const QModelIndex &index, int role) const
{
    std::lock_guard<decltype(mainQueueMutex)> lg(mainQueueMutex);
    //solution is here:
    if(static_cast<int>(filesQueue.size()) <= index.row())
        return QVariant();
    if ( role == Qt::DisplayRole) {
        return filesQueue[index.row()]->getFilename();
    }
}

и прошло 3 часа моей жизни, я никогда не вернусь :-)

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