Ruslan Polupan
April 17, 2019, 1:58 p.m.

Потоки. Параллельные действия.

QTableWidget, QThread, threads

Доброго времени суток!

Нужен совет по много поточности.
Есть вот такая вот табличка.

Столбец статуc хотелось бы получать в потоке для всех записей (АЗС).
Для одной записи я реализовал получение статуса сервера базы данных.

Вопрос в том как делать это для всех записей, количество записей до 200 чтобы для каждой записи это делалось в потоке.


Реализация получения статуа в потоке

  1. CheckAzsStatus *checkStatus = new CheckAzsStatus(modelConnections->data(modelConnections->index(0,0,QModelIndex())).toInt(),
  2. modelConnections->data(modelConnections->index(0,1,QModelIndex())).toString());
  3. QThread *thread = new QThread(this);
  4.  
  5. checkStatus->moveToThread(thread);
  6.  
  7.  
  8. connect(thread,&QThread::started, this, &ShowPage::slotStartExecute,Qt::DirectConnection);
  9. connect(thread,&QThread::started, checkStatus, &CheckAzsStatus::slotCheckAzsStatus,Qt::DirectConnection);
  10.  
  11.  
  12. connect(checkStatus,&CheckAzsStatus::signalSendResult,this,&ShowPage::slotGetAzsStatus);
  13.  
  14. connect(checkStatus,&CheckAzsStatus::finished,this,&ShowPage::slotStopExecute);
  15. connect(checkStatus,&CheckAzsStatus::finished,thread,&QThread::quit);
  16. connect(checkStatus,&CheckAzsStatus::finished,checkStatus,&CheckAzsStatus::deleteLater);
  17. connect(thread,&QThread::finished, thread, &QThread::deleteLater);
  18.  
  19. thread->start();
  20.  
  21.  
  22. }
  23.  
  24.  
  25. void ShowPage::slotStartExecute()
  26. {
  27. qInfo(logInfo()) << Q_FUNC_INFO << QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") << "Thread started";
  28. }
  29.  
  30. void ShowPage::slotStopExecute()
  31. {
  32. qInfo(logInfo()) << Q_FUNC_INFO << QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") << "Thread stoped";
  33. }
  34.  
  35. void ShowPage::slotGetAzsStatus(bool res)
  36. {
  37. if(res){
  38. ui->tableWidget->item(0,2)->setText("ON LINE");
  39. } else {
  40. ui->tableWidget->item(0,2)->setText("OFF LINE");
  41. }
  42.  
  43. }
  44.  

Реализация проверки
checkazsstatus.h

  1. #ifndef CHECKAZSSTATUS_H
  2. #define CHECKAZSSTATUS_H
  3.  
  4. #include <QObject>
  5.  
  6. class CheckAzsStatus : public QObject
  7. {
  8. Q_OBJECT
  9. public:
  10. explicit CheckAzsStatus(int term, QString ip, QObject *parent = nullptr);
  11.  
  12. signals:
  13. void signalSendResult(bool status);
  14. void finished(int term);
  15.  
  16. public slots:
  17. void slotCheckAzsStatus();
  18. private:
  19. QString m_serverName;
  20. int m_terminalID;
  21. };
  22.  
  23. #endif // CHECKAZSSTATUS_H
  24.  

checkazsstatus.cpp

  1. #include "checkazsstatus.h"
  2. #include "LoggingCategories/loggingcategories.h"
  3. #include <QTcpSocket>
  4.  
  5. CheckAzsStatus::CheckAzsStatus(int term, QString ip, QObject *parent) : QObject(parent)
  6. {
  7. m_serverName = ip;
  8. m_terminalID = term;
  9. qInfo(logInfo()) << "IP" << m_serverName << "Terminal" << m_terminalID;
  10. }
  11.  
  12. void CheckAzsStatus::slotCheckAzsStatus()
  13. {
  14.  
  15. bool status;
  16. QTcpSocket *tcpSocket = new QTcpSocket();
  17. tcpSocket->connectToHost(m_serverName, 3050);
  18. if(tcpSocket->waitForConnected(30000)){
  19. status = true;
  20. } else {
  21. status = false;
  22. }
  23. emit signalSendResult(status);
  24. emit finished(m_terminalID);
  25. }
  26.  

Буду благодарен за любою помошь.

5

Do you like it? Share on social networks!

25
Алексей Внуков
  • April 17, 2019, 2:03 p.m.

как то немного сомбурно выглядит описание. вы хотите получать статус 200 обьектов в отдельном потоке или для каждого свой поток? я так понял описание обьектов находится в бд?

    Ruslan Polupan
    • April 17, 2019, 2:15 p.m.

    Простите за сумбурность. :-)
    Да для каждого объекта в своем потоке, поскольку связь с объектами оставляет желать лучшего.
    При наличии связи с обектом на базе АЗС выполняется SQL запрос на получении информации ( в данном случае наименование видов топлива на АЗС)
    Описание объектов в объекте QSqlTableModel, получаемой из базы данных.

      Алексей Внуков
      • April 17, 2019, 2:36 p.m.

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

        nayk1982
        • April 17, 2019, 2:39 p.m.

        Придется в каждом потоке создавать свое соединение с БД: документация Qt

          Алексей Внуков
          • April 17, 2019, 2:50 p.m.
          • (edited)

          а смысл? соеденение с БД можно сделать глобальным и в потоке при запросе просто делать указание QSqlQuery query(str,db) документация

            Ruslan Polupan
            • April 17, 2019, 3:01 p.m.

            Алексей, данные об объектах я получаю.

            1. modelConnections = new QSqlQueryModel();
            2. // QSqlDatabase db = QSqlDatabase::database();
            3. QString inStr = m_listTerminals.join(",");
            4.  
            5.  
            6. QString strSQL = QString("SELECT c.TERMINAL_ID, c.SERVER_NAME, c.DB_NAME, c.CON_PASSWORD from CONNECTIONS c "
            7. "WHERE c.TERMINAL_ID IN (%1) and c.CONNECT_ID = 2 "
            8. "ORDER BY c.TERMINAL_ID")
            9. .arg(inStr);
            10. qInfo(logInfo()) << "SQL" << strSQL;
            11. modelConnections->setQuery(strSQL);
            12.  
            13. qInfo(logInfo()) << "Model Row Count" << modelConnections->rowCount();
            14.  
            15. ui->tableWidget->clearContents();
            16. ui->tableWidget->clear();
            17.  
            18. ui->tableWidget->setColumnCount(COLUMN_COUNT);
            19. ui->tableWidget->setHorizontalHeaderLabels(TABLE_COLUMN_LABELS);
            20. ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
            21. ui->tableWidget->verticalHeader()->hide();
            22.  
            23. for(int i = 0; i<modelConnections->rowCount();++i){
            24. ui->tableWidget->insertRow(i);
            25. ui->tableWidget->setItem(i,0,new QTableWidgetItem(modelConnections->data(modelConnections->index(i,0,QModelIndex())).toString()));
            26. ui->tableWidget->setItem(i,1,new QTableWidgetItem(modelConnections->data(modelConnections->index(i,1,QModelIndex())).toString()));
            27. ui->tableWidget->setItem(i,2, new QTableWidgetItem("Проверка"));
            28. }
            29.  
            30. ui->tableWidget->resizeColumnsToContents();

            Вопрос как раз в реализации. Перечитав кучу доков и форумов я только окончательно запутался. А хотелось бы просто посмотреть кусок рабочего кода. :-)

              nayk1982
              • April 17, 2019, 3:02 p.m.

              Это если в одном потоке. В параллельных потоках надо создавать соединение, причем с разными именами, пример:

              1. QSqlDatabase::addDatabase("QMYSQL", "db_thread1");
                Ruslan Polupan
                • April 17, 2019, 3:02 p.m.

                Это я в курсе, мне в потоке не надо пока подключатся к БД.

                  Ruslan Polupan
                  • April 17, 2019, 3:04 p.m.

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

                    Алексей Внуков
                    • April 17, 2019, 3:11 p.m.
                    • (edited)

                    проверено на практике - делаем глобальный

                    global.h

                    1. extern QSqlDatabase db;

                    global.cpp

                    1. QSqlDatabase db;

                    в основном классе

                    1. db=QSqlDatabase::addDatabase("QDB");
                    2. db.setDatabaseName(db_name);
                    3. db.setUserName(db_login);
                    4. db.setHostName(db_host);
                    5. db.setPassword(db_password)

                    и потом в любом месте в любом потоке через

                    1. QSqlQuery query(str,db);

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

                      Алексей Внуков
                      • April 17, 2019, 3:21 p.m.
                      • (edited)

                      примерно так (кусок реализации работы с системами Satel)
                      soket.h

                      1. #ifndef SOCKET_H
                      2. #define SOCKET_H
                      3.  
                      4. #include <QTcpSocket>
                      5. #include <QByteArray>
                      6. #include <QInputDialog>
                      7.  
                      8. class socket : public QObject
                      9. {
                      10. Q_OBJECT
                      11. public:
                      12. socket(const QString name, const QString host, const int port, QWidget *parent = nullptr);
                      13. QTcpSocket *psocket;
                      14. QString name;//??
                      15. QString host;
                      16. int port;
                      17.  
                      18. ~socket();
                      19.  
                      20. void connect_to_tcp ();
                      21. signals:
                      22. //void printmsg(QString);
                      23. void conect(socket* soc);
                      24. void slotReadyRead (socket* soc, QTcpSocket* tsoc);
                      25. void slotError (socket* soc, QTcpSocket* tsoc, QAbstractSocket::SocketError);
                      26. //void slotConnected (socket* soc);
                      27. };
                      28.  

                      soket.cpp

                      1. #include "socket.h"
                      2.  
                      3. socket::socket(const QString name, const QString host, const int port, QWidget *parent)
                      4. {
                      5. psocket=new QTcpSocket(this);
                      6. this->host=host;
                      7. this->port=port;
                      8. this->name=name;
                      9. connect(psocket, &QTcpSocket::connected, [this]{emit conect(this);});
                      10. connect(psocket, &QTcpSocket::readyRead, [this]{emit slotReadyRead(this,psocket);});
                      11. connect(psocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
                      12. [this](QAbstractSocket::SocketError socketError){emit slotError(this, psocket, socketError);});// SLOT(slotError(QAbstractSocket::SocketError)));
                      13. }
                      14.  
                      15. socket::~socket()
                      16. {
                      17.  
                      18. }
                      19.  
                      20. void socket::connect_to_tcp()
                      21. {
                      22. psocket->connectToHost(host,port);
                      23. }

                      в основном рабочем классе делал так

                      1. QSqlQuery query_m("SELECT name, ip_address, port FROM mod_satel");;
                      2. while (query_m.next()) {
                      3. QString name=query_m.value(0).toString();
                      4. QString host=query_m.value(1).toString();
                      5. int i = query_m.value(2).toInt();
                      6. //qDebug() << "Adding new soket";
                      7. socket* p_socket=new socket(name,host,i);
                      8. connect(p_socket,&socket::conect, this,&satel::slotConnected_satel);
                      9. connect(p_socket,&socket::slotReadyRead,this,&satel::slotReadyRead_satel);
                      10. connect(p_socket,&socket::slotError,this,&satel::slotError_satel);
                      11. m_socket.append(p_socket);
                      12. }

                      идею взял вот с этой книжки "Lazar G., Penea R. - Mastering Qt 5 - 2016"

                        Ruslan Polupan
                        • April 17, 2019, 3:25 p.m.

                        Спасибо, но меня больше интересует реализация потоков.

                          Алексей Внуков
                          • April 17, 2019, 3:38 p.m.
                          • (edited)

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

                          1. if(vector.contains(N))
                          2. {
                          3. QtConcurrent::run(this, &satel::arm_disarm, buffer);
                          4. }
                            Алексей Внуков
                            • April 17, 2019, 3:49 p.m.
                            • (edited)

                            ну или попробывать такой вариант(ваш обьект в другом потоке останется жить пока не удалите)

                            1. QSqlQuery query_m("SELECT name, ip_address, port FROM mod_satel");;
                            2. while (query_m.next()) {
                            3. QString name=query_m.value(0).toString();
                            4. QString host=query_m.value(1).toString();
                            5. int i = query_m.value(2).toInt();
                            6. //qDebug() << "Adding new soket";
                            7. socket* p_socket=new socket(name,host,i);
                            8. connect(p_socket,&socket::conect, this,&satel::slotConnected_satel);
                            9. connect(p_socket,&socket::slotReadyRead,this,&satel::slotReadyRead_satel);
                            10. connect(p_socket,&socket::slotError,this,&satel::slotError_satel);
                            11. QThread *thread=new QThread(this);
                            12. p_socket->moveToThread(thread);
                            13. thread->start();
                            14. }
                              R
                              • April 18, 2019, 3:05 a.m.
                              • (edited)

                              Добрий вечір, не рекомендував би вам використоувати для цих цілей, багато потоків, один потік це приблизно 67мб (принаймні було в моєму випадку) і того вам на 200 потоків потрібно не мало памяті,
                              моя реалізація стосується роботи з QWebSocket короткий код нижче, все перевірено і працює, + не потрібно робити багато конекшинів з кожного потоку до бази, і не так важко реалізувати комунікацію

                              1. QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
                              2.  
                              3. QString id_socket = QString("%1%2").arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz")).arg("blablabla");
                              4.  
                              5. pSocket->setObjectName(id_socket);
                              6. UserConnect* userThread = new UserConnect(pSocket);
                              7. connect(pSocket, &QWebSocket::disconnected, this, &SSLWebSocketServer::socketDisconnected);
                              8. connect(pSocket, &QWebSocket::disconnected, userThread, &UserConnect::deleteLater);
                              9.  
                              10. // цей параметр з іншого класу, але дляприкладу привів
                              11. QMap<QString, QWebSocket *> clients;
                              12.  
                              13. lients.insert(id_socket, pSocket);
                              14.  
                              15.  
                              1. далі можна робити комунікації по ObjectName
                              2.  
                              3. по типу
                              1. QWebSocket *pSocket = clients.value(id_socket);
                              2.  
                              3. if(pSocket)
                              4. {
                              5. pSocket->sendTextMessage(message);
                              6. }
                                u
                                • April 18, 2019, 8:20 a.m.

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

                                  Алексей Внуков
                                  • April 18, 2019, 12:21 p.m.

                                  я не знаю как у вас получилось 1поток=67мб (может у вас был не поток а процесс?). у меня есть проект, в нем постоянно активно 28 потоков, с таймерами и бесконечными циклами, и они регулярно стучат в БД,так вот там у меня с запущенным интерфейсом (QML) занято 105м,, без интерфейса все 42мб

                                    Ruslan Polupan
                                    • April 18, 2019, 12:29 p.m.
                                    • (edited)

                                    Большое спасибо за наводящие ответы. :-)
                                    QTcpSocket мне нужен всего лишь для проверки доступна ли удаленная база данных.
                                    Если доступна, то тогда у же к ней будем обращаться с запросами.

                                      Алексей Внуков
                                      • April 18, 2019, 12:33 p.m.

                                      отпишитесь потом по результатам, что получилось сколько занимает, и какая нагрузка в общем

                                        Ruslan Polupan
                                        • April 18, 2019, 12:47 p.m.

                                        Следующий этап это выполнение на каждой активной базе выполнять в потоке запрос

                                        1. select t.TANK_ID, f.NAME from tanks t
                                        2. left join FUELS f on f.FUEL_ID = t.FUEL_ID
                                        3. where t.ISACTIVE = 'T'

                                        Вот там и будем мерять.
                                        Потому что статистика выволнения не очень впечатляет :-)

                                        Executing statement...
                                        Statement executed (elapsed time: 0.000s).
                                        360 fetches, 19 marks, 7 reads, 16 writes.
                                        0 inserts, 0 updates, 0 deletes, 33 index, 14 seq.
                                        Delta memory: 200 bytes.
                                        Total execution time: 5.026s
                                        Script execution finished.

                                        Кстати вопрос на эту тему. QSqlQuery позволяет какимно образом получать такую информаци?

                                          Ruslan Polupan
                                          • April 18, 2019, 7:17 p.m.

                                          Реаизовал с использованием QtConcurent, дабы получить сигнал что все потоки по опросу порта завершились.

                                          1. checkOnline = new QFutureWatcher<bool>(this);
                                          2. connect(checkOnline,&QFutureWatcher<QTcpSocket>::started, this, &ShowPage::slotStartExecute);
                                          3. connect(checkOnline,&QFutureWatcher<QTcpSocket>::resultReadyAt,this,&ShowPage::slotStopExecute);
                                          4. connect(checkOnline,&QFutureWatcher<QTcpSocket>::finished,this,&ShowPage::slotFinished);
                                          5.  
                                          6. std::function<bool(QString)> getOnline = [] (const QString ipAZS){
                                          7. QTcpSocket tcpSocket;
                                          8. tcpSocket.connectToHost(ipAZS,3050);
                                          9. return tcpSocket.waitForConnected(30000);
                                          10. };
                                          11.  
                                          12. checkOnline->setFuture(QtConcurrent::mapped(m_listIP,getOnline));
                                          13. }
                                          14.  
                                          15.  
                                          16. void ShowPage::slotStartExecute()
                                          17. {
                                          18. qInfo(logInfo()) << Q_FUNC_INFO << QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") << "Thread started";
                                          19. }
                                          20.  
                                          21. void ShowPage::slotStopExecute(int term)
                                          22. {
                                          23. if(checkOnline->resultAt(term)) {
                                          24. ui->tableWidget->item(term, 2)->setText("ON LINE");
                                          25. ui->tableWidget->item(term,2)->setTextColor("Green");
                                          26. ui->tableWidget->item(term, 2)->setIcon(QIcon(":/Icons/connect.png"));
                                          27. } else {
                                          28. ui->tableWidget->item(term, 2)->setText("OFF LINE");
                                          29. ui->tableWidget->item(term, 2)->setTextColor("Red");
                                          30. ui->tableWidget->item(term, 2)->setIcon(QIcon(":/Icons/disconnect.png"));
                                          31. }
                                          32. }
                                          33.  
                                          34. void ShowPage::slotGetAzsStatus(bool res)
                                          35. {
                                          36. isOnline = res;
                                          37. }
                                          38.  
                                          39. void ShowPage::slotFinished()
                                          40. {
                                          41. qInfo(logInfo()) << "QtConcurent finished";
                                          42. }
                                            Ruslan Polupan
                                            • April 18, 2019, 7:47 p.m.

                                            отпишитесь потом по результатам, что получилось сколько занимает, и какая нагрузка в общем

                                            А как мерять :-)

                                              Алексей Внуков
                                              • April 19, 2019, 1:49 p.m.

                                              просто скажите что по загрузке процессора и оперативы, когда происходит опрос

                                                Ruslan Polupan
                                                • April 19, 2019, 3:43 p.m.

                                                Можно просмотреть результаты тут
                                                https://drive.google.com/file/d/1tFmrljPrxjqkb6OZSvCmF5uGtuHvlKPq/view?usp=sharing

                                                  R
                                                  • April 19, 2019, 3:55 p.m.

                                                  мені важко це зараз навіть перевірити, тому що знайшов коміт, це ще було в 2016 році, і цей код не буде працювати коректно зараз, єдине скажу що це були QThread

                                                    Comments

                                                    Only authorized users can post comments.
                                                    Please, Log in or Sign up
                                                    • Last comments
                                                    • AK
                                                      April 1, 2025, 11:41 a.m.
                                                      Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
                                                    • Evgenii Legotckoi
                                                      March 9, 2025, 9:02 p.m.
                                                      К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                                                    • VP
                                                      March 9, 2025, 4:14 p.m.
                                                      Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                                                    • ИМ
                                                      Nov. 22, 2024, 9:51 p.m.
                                                      Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                                                    • Evgenii Legotckoi
                                                      Oct. 31, 2024, 11:37 p.m.
                                                      Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup