A
AlexTeosМамыр 31, 2018, 9: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".

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

Спасибо!
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

11
Evgenii Legotckoi
  • Маусым 1, 2018, 2:37 Т.Қ.

Добрый день!

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

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

Насчёт моделей, мы даже в своём проекте на работе пока не используем многопоточные реализации запросов к базе данных...
Хотя возможно это связано с тем, что мы используем Wt::dbo для работы с базой данных, возможно, что эта ORM работает более быстро, хотя конечно подвисания интерфейса в некоторых случаях тоже есть, но в нашем случае это не критично.
    A
    • Маусым 1, 2018, 3:12 Т.Қ.
    • (өңделген)
    Рассмотрим 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 я использую только в том же потоке где и создаю модели.
     
    Да, мьютексы я думаю решили бы проблему. Но их использование кажется мне не очень уместным, это все же БД, я думаю она должна поддерживать несколько подключений.
      Evgenii Legotckoi
      • Маусым 1, 2018, 6:55 Т.Қ.

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

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

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

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

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

        A
        • Маусым 2, 2018, 6:07 Т.Ж.
        • (өңделген)

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


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

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

        Еще для меня не очевиден момент создания модели, почему мы указываем запрос, но не указываем бд и подключение? Что если баз данных несколько? У приложения где то внутри есть пул подключений и баз данных? Может в этом и есть проблема что модель и ядро используют одно подключение?
          Evgenii Legotckoi
          • Маусым 4, 2018, 3:32 Т.Ж.

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


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

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

          По факту работа с базой данных в Qt не самая сильная, ОРМ например нет вовсе, только модели данных, лишь бы было и сырые запросы.
            A
            • Маусым 4, 2018, 8:15 Т.Ж.

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

              Evgenii Legotckoi
              • Маусым 4, 2018, 8:26 Т.Ж.

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


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

              Например, этот сайт на данный момент использует одновременно три своих инстанса для обслуживания пользователей и соответсвенно три коннекта к базе данных.
                A
                • Маусым 4, 2018, 8:30 Т.Ж.

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

                  Evgenii Legotckoi
                  • Маусым 4, 2018, 8:32 Т.Ж.

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

                    A
                    • Маусым 4, 2018, 8:40 Т.Ж.
                    • (өңделген)

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


                    Мьютекс же пришлось бы прокидывать через все приложение для GUI, и дважды перепроверять везде ли его использовали, ну и производительность будет точно не лучше отдельных подключений.
                      Evgenii Legotckoi
                      • Маусым 4, 2018, 8:43 Т.Ж.

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

                        Пікірлер

                        Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
                        Кіріңіз немесе Тіркеліңіз
                        Г

                        C++ - Тест 001. Первая программа и типы данных

                        • Нәтиже:66ұпай,
                        • Бағалау ұпайлары-1
                        t

                        C++ - Тест 001. Первая программа и типы данных

                        • Нәтиже:33ұпай,
                        • Бағалау ұпайлары-10
                        t

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

                        • Нәтиже:52ұпай,
                        • Бағалау ұпайлары-4
                        Соңғы пікірлер
                        G
                        GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
                        Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
                        d
                        dblas5Шілде 5, 2024, 11:02 Т.Ж.
                        QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                        k
                        kmssrАқп. 8, 2024, 6:43 Т.Қ.
                        Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                        АК
                        Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
                        Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                        Енді форумда талқылаңыз
                        Evgenii Legotckoi
                        Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
                        добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                        F
                        FynjyШілде 22, 2024, 4:15 Т.Ж.
                        при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
                        BlinCT
                        BlinCTМаусым 25, 2024, 1 Т.Ж.
                        Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
                        BlinCT
                        BlinCTМамыр 5, 2024, 5:46 Т.Ж.
                        Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
                        Evgenii Legotckoi
                        Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
                        Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

                        Бізді әлеуметтік желілерде бақылаңыз