Политика конфиденциальностиКонтактыО сайтеОтзывыGitHubDonate
© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB
A
31 мая 2018 г. 13:10

Использование QSqlQueryModel и базы данных в разных потоках.

Qt, QML, SQLite, QSqlQueryModel

QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'Добрый день!


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

Теперь о проблеме с которой я столкнулся. Я реализую приложение где основная логика заложена в "ядро", а GUI в двух версиях на QML и на виджетах подключается к ядру с помощью представителя(MVP). Ядро переносится представителем в отдельный поток. У ядра есть модуль подключения к базе данных, соответственно с GUI он находится в разных потоках. И проблема с которой я столкнулся - в каком из потоков должны работать модели?

Изначально я создавал модели в потоке GUI, и при одновременном заполнении БД и попытке работать с ней из GUI постоянно сыпались ошибки. Перенеся создание моделей в модуль подключения к БД версия на виджетах стала работать стабильно, но версия для QML стала работать не корректно - появлялась ошибка QObject::connect: Cannot queue arguments of type 'QQmlChangeSet'
с которой я не первый кто сталкивается, но ее причины я не понял. Возникает она при вызове метода setQuery,  и гробит элементы ListView.

Для формирования "таблицы" из QSqlQueryModel - использую код отсюда - https://wiki.qt.io/How_to_Use_a_QSqlQueryModel_in_QML из раздела "A more generic approach".

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

Спасибо!
11

Добрый день!

Спасибо за ваш отзыв, очень приятно.

А теперь к делу.
Вы не могли бы показать ключевые моменты вашего кода.
Как переносится ядро в отдельный поток, а также, что находится в методе setQuery.
Скорее всего не хватает мьютексов.

Насчёт моделей, мы даже в своём проекте на работе пока не используем многопоточные реализации запросов к базе данных...
Хотя возможно это связано с тем, что мы используем Wt::dbo для работы с базой данных, возможно, что эта ORM работает более быстро, хотя конечно подвисания интерфейса в некоторых случаях тоже есть, но в нашем случае это не критично.
0
A
Рассмотрим QML версию. Представитель переносит ядро в отдельный поток:
 
core->moveToThread(&core_thread);
connect(&core_thread, SIGNAL(started()), core, SLOT(start()));
core_thread.start();
 
Затем в методе start происходит подключение к бд:
 
QSqlDatabase::addDatabase("QSQLITE");
 
Далее модели создаются либо тут же(в потоке ядра и бд) и сигналом передаются представителю который устанавливает их в контекст:
 
context->setContextProperty("model", model);
 
либо создаются в представителе(и находятся в потоке GUI) и так же устанавливаются в контекст. Затем уже интерфейс обновляет модели меняя параметры в запросах(с помощью setQuery), а ядро в свою очередь наполняет БД. 
 
setQuery (код который я приводил по ссылке выше):
void SqlQueryModel::setQuery(const QSqlQuery & query)
{
    QSqlQueryModel::setQuery(query);
    generateRoleNames();
}
void SqlQueryModel::generateRoleNames()
{
    m_roleNames.clear();
    for( int i = 0; i < record().count(); i ++) {
        m_roleNames.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
    }
}
Причем setQuery я использую только в том же потоке где и создаю модели.
 
Да, мьютексы я думаю решили бы проблему. Но их использование кажется мне не очень уместным, это все же БД, я думаю она должна поддерживать несколько подключений.
0

с мьютексами вопрос не в том, что БД не справится, это понятно, что БД должна это разрулить у себя.

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

Здесь очень скользкий момент получается с данными и моделями в рамках QML.

Если у модели не устанавливается parent в C++, то тогда parent устанавливается в QML контексте, и тогда модель принадлежит сборщику мусора. Подробнее в этой статье . А когда модель оказывается в QML, то фактически она оказывается в GUI потоке и уже то, что она была создана в ином потоке уже не хорошо.

Я бы попробовал делать сырой запрос через QSqlQuery, формировать результат и уже это добро устанавливать в качестве вектора объектов в модель. Передавать объекты при этом через сигнал-слотовое соединение и обязательно использовать мьютексы, чтобы не угробить данные.

0
A

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


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

Как ядро может свернуть себе шею? Оно добавляет запись, бд производит транзакцию. Даже если в этот же момент модель производит обновление, она делает свою транзакцию, и уже БД своими светофорами контролирует что бы они друг другу не помешали.

Еще для меня не очевиден момент создания модели, почему мы указываем запрос, но не указываем бд и подключение? Что если баз данных несколько? У приложения где то внутри есть пул подключений и баз данных? Может в этом и есть проблема что модель и ядро используют одно подключение?
0

QSqlQueryModel в принципе при каждом обновлении тащит заново все записи... так что в любом случае большой объём данных пересылается.
Там разве что можно оптимизировать запросы и догружать данные при сортировки используя информацию и о первом и последнем запросе в выборке... В общем здесь большоё поле жкспериментов для раздумий...


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

А насчёт подключения БД, то когда вы задаёте подключение к базе данных, например, как в классе DataBase в этой статье (Метод openDataBase), то информация о подключении сохраняется в статическом контейнере (уже не помню, что там именнно вектор или map, какой-нибудь) в недрах модуля Qt SQL. Поэтому все эти запросы QSqlQuery и работают нормально, во всяком случае с одним подключением к одной базе данных проблем не возникает.
Это всё глубоко зарыто в pimpl QSqlDataBase, если скачаете исходники Qt, то можете посмотреть насколько глубоко это там хранится.

По факту работа с базой данных в Qt не самая сильная, ОРМ например нет вовсе, только модели данных, лишь бы было и сырые запросы.
0
A

Вроде как удалось найти решение. Судя по всему проблема была именно в одном подключении. Сейчас я создаю модели в GUI потоке и для них создаю отдельное подключение через addDatabase с указанием имени подключения. В результате приложение работает стабильно, и как косвенный признак - при обновлении модели с большим количеством записей ядру временно не удается вставить новую запись в БД.

0

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


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

Например, этот сайт на данный момент использует одновременно три своих инстанса для обслуживания пользователей и соответсвенно три коннекта к базе данных.
0
A

Да, я именно так и сделал, два подключения: одно для ядра, другое для GUI. Раньше два потока лезли в одно подключение и все ломали, а сейчас встают в очередь и ждут когда БД разблокируется.

0

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

0
A

Ну 2 подключения все таки логически оправданы. 2 отдельных компонента используют одну БД по своему усмотрению, одновременное использование БД контролируется драйвером.


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

Да, согласен с Вами

0

Ответы

Только авторизованные пользователи могут отвечать на форуме.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
ДД
13 декабря 2018 г. 16:24
Дмитрий Дубовик

C++ - Тест 005. Структуры и Классы

  • Результат:66баллов,
  • Очки рейтинга-1
13 декабря 2018 г. 16:04
Metelev

Qt - Тест 001. Сигналы и слоты

  • Результат:47баллов,
  • Очки рейтинга-6
YC
12 декабря 2018 г. 18:49
Yaroslav Chernetskyi

Qt - Тест 001. Сигналы и слоты

  • Результат:31баллов,
  • Очки рейтинга-10
Последние комментарии
V
15 декабря 2018 г. 2:06
Vlad15007

Спасибо большое!Очень помогли!
11 декабря 2018 г. 21:01
Евгений Легоцкой

Не знаю, какой-там конкретно эффект и если честно не хочется fl studio ради того, чтобы посмотреть устанавливать, но из того, что увидел в интернете. Предполагаю, что то, что вы хотите с...
V
11 декабря 2018 г. 19:25
Vlad15007

Подскажите пожалуйста ( я новичок совсем)Можно ли организовать спрайт без этого окошка (как в fl studio fruity dance)?
11 декабря 2018 г. 15:06
Евгений Легоцкой

Что интересно, если написать так from <application_name>.<module_name> import <filename> ,то PyCharm сносит крышу, если разрабатываешь в рамках проекта приложение, ко...
11 декабря 2018 г. 14:52
Илья Чичак

Тут мне тоже есть что сказать=) Сами разрабы советуют импортировать следующим образом: from <application_name> import <module_name> Стоит избегать from . import &l...;
Сейчас обсуждают на форуме
ИМ
18 декабря 2018 г. 15:29
Игорь Максимов

Доброго времени суток. Имеется модель для видео-контента (Movie), а конкретно привязана сейчас к одной модели(Compilation). Появилась необходимость добавить еще одну модель (Category) и связа...
R
18 декабря 2018 г. 12:25
RED_Spider

именно так, проблема в кодировке, а именно в отсутствии шрифтов на сервере, для меня вопрос решился в CentOS 7yum install curl cabextract xorg-x11-font-utils fontconfig всем спасибо за ...
U
18 декабря 2018 г. 10:39
Unreal_man

А вот этот коннект здесь и вовсе не нужен connect(ui->ok3, &QPushButton::clicked, this, &Widget::addToText); А как же без него? ============================== ...
m
17 декабря 2018 г. 19:03
melnik10

Спасибо, попробую!
R
16 декабря 2018 г. 14:41
RED_Spider

перевірено все працює http://doc.qt.io/qt-5/appicon.html Setting the Application Icon on Windows First, create an ICO format bitmap file that contains the icon image. This ca...
Присоединяйтесь к нам в социальных сетях

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы