Ruslan Polupan
Ruslan Polupan17 апреля 2019 г. 3:58

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

QTableWidget, QThread, threads

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

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

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

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


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

CheckAzsStatus *checkStatus = new CheckAzsStatus(modelConnections->data(modelConnections->index(0,0,QModelIndex())).toInt(),
                                                     modelConnections->data(modelConnections->index(0,1,QModelIndex())).toString());
    QThread *thread = new QThread(this);

    checkStatus->moveToThread(thread);


    connect(thread,&QThread::started, this, &ShowPage::slotStartExecute,Qt::DirectConnection);
    connect(thread,&QThread::started, checkStatus, &CheckAzsStatus::slotCheckAzsStatus,Qt::DirectConnection);


    connect(checkStatus,&CheckAzsStatus::signalSendResult,this,&ShowPage::slotGetAzsStatus);

    connect(checkStatus,&CheckAzsStatus::finished,this,&ShowPage::slotStopExecute);
    connect(checkStatus,&CheckAzsStatus::finished,thread,&QThread::quit);
    connect(checkStatus,&CheckAzsStatus::finished,checkStatus,&CheckAzsStatus::deleteLater);
    connect(thread,&QThread::finished, thread, &QThread::deleteLater);

    thread->start();


}


void ShowPage::slotStartExecute()
{
    qInfo(logInfo()) << Q_FUNC_INFO << QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") <<  "Thread started";
}

void ShowPage::slotStopExecute()
{
    qInfo(logInfo()) << Q_FUNC_INFO << QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") <<  "Thread stoped";
}

void ShowPage::slotGetAzsStatus(bool res)
{
    if(res){
        ui->tableWidget->item(0,2)->setText("ON LINE");
    } else {
        ui->tableWidget->item(0,2)->setText("OFF LINE");
    }

}

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

#ifndef CHECKAZSSTATUS_H
#define CHECKAZSSTATUS_H

#include <QObject>

class CheckAzsStatus : public QObject
{
    Q_OBJECT
public:
    explicit CheckAzsStatus(int term, QString ip, QObject *parent = nullptr);

signals:
    void signalSendResult(bool status);
    void finished(int term);

public slots:
    void slotCheckAzsStatus();
private:
    QString m_serverName;
    int m_terminalID;
};

#endif // CHECKAZSSTATUS_H

checkazsstatus.cpp

#include "checkazsstatus.h"
#include "LoggingCategories/loggingcategories.h"
#include <QTcpSocket>

CheckAzsStatus::CheckAzsStatus(int term, QString ip, QObject *parent) : QObject(parent)
{
    m_serverName = ip;
    m_terminalID = term;
    qInfo(logInfo()) << "IP" << m_serverName << "Terminal" << m_terminalID;
}

void CheckAzsStatus::slotCheckAzsStatus()
{

    bool status;
    QTcpSocket *tcpSocket = new QTcpSocket();
    tcpSocket->connectToHost(m_serverName, 3050);
    if(tcpSocket->waitForConnected(30000)){
        status = true;
    } else {
        status = false;
    }
    emit signalSendResult(status);
    emit finished(m_terminalID);
}

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

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

Вам это нравится? Поделитесь в социальных сетях!

25
Алексей Внуков
  • 17 апреля 2019 г. 4:03

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

    Ruslan Polupan
    • 17 апреля 2019 г. 4:15

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

      Алексей Внуков
      • 17 апреля 2019 г. 4:36

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

        nayk1982
        • 17 апреля 2019 г. 4:39

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

          Алексей Внуков
          • 17 апреля 2019 г. 4:50
          • (ред.)

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

            Ruslan Polupan
            • 17 апреля 2019 г. 5:01

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

                modelConnections = new QSqlQueryModel();
            //    QSqlDatabase db = QSqlDatabase::database();
                QString inStr = m_listTerminals.join(",");
            
            
                QString strSQL = QString("SELECT c.TERMINAL_ID, c.SERVER_NAME, c.DB_NAME, c.CON_PASSWORD from CONNECTIONS c "
                                         "WHERE c.TERMINAL_ID IN (%1) and c.CONNECT_ID = 2 "
                                         "ORDER BY c.TERMINAL_ID")
                        .arg(inStr);
                qInfo(logInfo()) << "SQL" << strSQL;
                modelConnections->setQuery(strSQL);
            
                qInfo(logInfo()) << "Model Row Count" << modelConnections->rowCount();
            
                ui->tableWidget->clearContents();
                ui->tableWidget->clear();
            
                ui->tableWidget->setColumnCount(COLUMN_COUNT);
                ui->tableWidget->setHorizontalHeaderLabels(TABLE_COLUMN_LABELS);
                ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
                ui->tableWidget->verticalHeader()->hide();
            
                for(int i = 0; i<modelConnections->rowCount();++i){
                    ui->tableWidget->insertRow(i);
                    ui->tableWidget->setItem(i,0,new QTableWidgetItem(modelConnections->data(modelConnections->index(i,0,QModelIndex())).toString()));
                    ui->tableWidget->setItem(i,1,new QTableWidgetItem(modelConnections->data(modelConnections->index(i,1,QModelIndex())).toString()));
                    ui->tableWidget->setItem(i,2, new QTableWidgetItem("Проверка"));
                }
            
                ui->tableWidget->resizeColumnsToContents();
            

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

              nayk1982
              • 17 апреля 2019 г. 5:02

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

              QSqlDatabase::addDatabase("QMYSQL", "db_thread1");
              
                Ruslan Polupan
                • 17 апреля 2019 г. 5:02

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

                  Ruslan Polupan
                  • 17 апреля 2019 г. 5:04

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

                    Алексей Внуков
                    • 17 апреля 2019 г. 5:11
                    • (ред.)

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

                    global.h

                    extern QSqlDatabase db;
                    

                    global.cpp

                    QSqlDatabase db;
                    

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

                     db=QSqlDatabase::addDatabase("QDB");
                     db.setDatabaseName(db_name);
                     db.setUserName(db_login);
                     db.setHostName(db_host);
                     db.setPassword(db_password)
                    

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

                    QSqlQuery query(str,db);
                    

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

                      Алексей Внуков
                      • 17 апреля 2019 г. 5:21
                      • (ред.)

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

                      #ifndef SOCKET_H
                      #define SOCKET_H
                      
                      #include <QTcpSocket>
                      #include <QByteArray>
                      #include <QInputDialog>
                      
                      class socket : public QObject
                      {
                          Q_OBJECT
                      public:
                          socket(const QString name, const QString host, const int port, QWidget *parent = nullptr);
                          QTcpSocket *psocket;
                          QString name;//??
                          QString host;
                          int port;
                      
                          ~socket();
                      
                          void connect_to_tcp  ();
                      signals:
                          //void printmsg(QString);
                          void conect(socket* soc);
                          void slotReadyRead (socket* soc, QTcpSocket* tsoc);
                          void slotError (socket* soc, QTcpSocket* tsoc, QAbstractSocket::SocketError);
                          //void slotConnected   (socket* soc);
                      };
                      
                      

                      soket.cpp

                      #include "socket.h"
                      
                      socket::socket(const QString name, const QString host, const int port, QWidget *parent)
                      {
                          psocket=new QTcpSocket(this);
                          this->host=host;
                          this->port=port;
                          this->name=name;
                          connect(psocket, &QTcpSocket::connected, [this]{emit conect(this);});
                          connect(psocket, &QTcpSocket::readyRead, [this]{emit slotReadyRead(this,psocket);});
                          connect(psocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
                                  [this](QAbstractSocket::SocketError socketError){emit slotError(this, psocket, socketError);});// SLOT(slotError(QAbstractSocket::SocketError)));
                      }
                      
                      socket::~socket()
                      {
                      
                      }
                      
                      void socket::connect_to_tcp()
                      {
                          psocket->connectToHost(host,port);
                      }
                      

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

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

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

                        Ruslan Polupan
                        • 17 апреля 2019 г. 5:25

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

                          Алексей Внуков
                          • 17 апреля 2019 г. 5:38
                          • (ред.)

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

                           if(vector.contains(N))
                          {
                              QtConcurrent::run(this, &satel::arm_disarm, buffer);
                          }
                          
                            Алексей Внуков
                            • 17 апреля 2019 г. 5:49
                            • (ред.)

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

                             QSqlQuery query_m("SELECT name, ip_address, port FROM mod_satel");;
                                    while (query_m.next()) {
                                        QString name=query_m.value(0).toString();
                                        QString host=query_m.value(1).toString();
                                        int i = query_m.value(2).toInt();
                                        //qDebug() << "Adding new soket";
                                        socket* p_socket=new socket(name,host,i);
                                        connect(p_socket,&socket::conect, this,&satel::slotConnected_satel);
                                        connect(p_socket,&socket::slotReadyRead,this,&satel::slotReadyRead_satel);
                                        connect(p_socket,&socket::slotError,this,&satel::slotError_satel);
                                       QThread *thread=new QThread(this);
                                       p_socket->moveToThread(thread);
                                       thread->start();
                                    }
                            
                              R
                              • 17 апреля 2019 г. 17:05
                              • (ред.)

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

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

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

                                  Алексей Внуков
                                  • 18 апреля 2019 г. 2:21

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

                                    Ruslan Polupan
                                    • 18 апреля 2019 г. 2:29
                                    • (ред.)

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

                                      Алексей Внуков
                                      • 18 апреля 2019 г. 2:33

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

                                        Ruslan Polupan
                                        • 18 апреля 2019 г. 2:47

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

                                        select t.TANK_ID, f.NAME from tanks t
                                        left join FUELS f on f.FUEL_ID = t.FUEL_ID
                                        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
                                          • 18 апреля 2019 г. 9:17

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

                                              checkOnline = new QFutureWatcher<bool>(this);
                                              connect(checkOnline,&QFutureWatcher<QTcpSocket>::started, this, &ShowPage::slotStartExecute);
                                              connect(checkOnline,&QFutureWatcher<QTcpSocket>::resultReadyAt,this,&ShowPage::slotStopExecute);
                                              connect(checkOnline,&QFutureWatcher<QTcpSocket>::finished,this,&ShowPage::slotFinished);
                                          
                                              std::function<bool(QString)> getOnline = [] (const QString ipAZS){
                                                  QTcpSocket tcpSocket;
                                                  tcpSocket.connectToHost(ipAZS,3050);
                                                  return tcpSocket.waitForConnected(30000);
                                              };
                                          
                                              checkOnline->setFuture(QtConcurrent::mapped(m_listIP,getOnline));
                                          }
                                          
                                          
                                          void ShowPage::slotStartExecute()
                                          {
                                             qInfo(logInfo()) << Q_FUNC_INFO << QDateTime::currentDateTime().toString("dd-MM-yyyy hh:mm:ss.zzz") <<  "Thread started";
                                          }
                                          
                                          void ShowPage::slotStopExecute(int term)
                                          {
                                              if(checkOnline->resultAt(term)) {
                                                  ui->tableWidget->item(term, 2)->setText("ON LINE");
                                                  ui->tableWidget->item(term,2)->setTextColor("Green");
                                                  ui->tableWidget->item(term, 2)->setIcon(QIcon(":/Icons/connect.png"));
                                              } else {
                                                  ui->tableWidget->item(term, 2)->setText("OFF LINE");
                                                  ui->tableWidget->item(term, 2)->setTextColor("Red");
                                                  ui->tableWidget->item(term, 2)->setIcon(QIcon(":/Icons/disconnect.png"));
                                              }
                                          }
                                          
                                          void ShowPage::slotGetAzsStatus(bool res)
                                          {
                                              isOnline = res;
                                          }
                                          
                                          void ShowPage::slotFinished()
                                          {
                                              qInfo(logInfo()) << "QtConcurent finished";
                                          }
                                          
                                            Ruslan Polupan
                                            • 18 апреля 2019 г. 9:47

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

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

                                              Алексей Внуков
                                              • 19 апреля 2019 г. 3:49

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

                                                Ruslan Polupan
                                                • 19 апреля 2019 г. 5:43

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

                                                  R
                                                  • 19 апреля 2019 г. 5:55

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

                                                    Комментарии

                                                    Только авторизованные пользователи могут публиковать комментарии.
                                                    Пожалуйста, авторизуйтесь или зарегистрируйтесь
                                                    г
                                                    • ги
                                                    • 23 апреля 2024 г. 22:51

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

                                                    • Результат:41баллов,
                                                    • Очки рейтинга-8
                                                    l
                                                    • laei
                                                    • 23 апреля 2024 г. 16:19

                                                    C++ - Тест 004. Указатели, Массивы и Циклы

                                                    • Результат:10баллов,
                                                    • Очки рейтинга-10
                                                    l
                                                    • laei
                                                    • 23 апреля 2024 г. 16:17

                                                    C++ - Тест 003. Условия и циклы

                                                    • Результат:50баллов,
                                                    • Очки рейтинга-4
                                                    Последние комментарии
                                                    k
                                                    kmssr9 февраля 2024 г. 2:43
                                                    Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                                                    АК
                                                    Анатолий Кононенко5 февраля 2024 г. 9:50
                                                    Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                                                    EVA
                                                    EVA25 декабря 2023 г. 18:30
                                                    Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                                                    J
                                                    JonnyJo25 декабря 2023 г. 16:38
                                                    Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                                                    G
                                                    Gvozdik19 декабря 2023 г. 5:01
                                                    Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                                                    Сейчас обсуждают на форуме
                                                    G
                                                    Gar22 апреля 2024 г. 12:46
                                                    Clipboard Как скопировать окно целиком в clipb?
                                                    DA
                                                    Dr Gangil Academics20 апреля 2024 г. 14:45
                                                    Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
                                                    a
                                                    a_vlasov14 апреля 2024 г. 13:41
                                                    Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
                                                    Павел Дорофеев
                                                    Павел Дорофеев14 апреля 2024 г. 9:35
                                                    QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
                                                    f
                                                    fastrex4 апреля 2024 г. 11:47
                                                    Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

                                                    Следите за нами в социальных сетях