Russian Qt Forum
Март 28, 2024, 20:40 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Как закрыть поток и очистить выделенную под него память в деструкторе?  (Прочитано 13479 раз)
Павелъ
Гость
« : Ноябрь 13, 2017, 12:49 »

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

Имею дерево (QTreeView), которое содержит список сетей: 255.255,
Для каждой сети сегментов: 255.255.255,
Для каждого сегмента ip-адресов: 255.255.255.255

При клике на Item, отвечающий за сегмент, открывается QTableView, при открытии этой таблицы создаётся модель, содержащая строки с ip-адресами, и некоторой информацией об этом устройстве. Самый первый Item имеет тип IpStandardItemWithThread, наследуемый от QStandardItem и QObject.
При клике на другой item в дереве – строки в QTableView удаляются.

Суть в чём. В классе IpStandardItemWithThread я создаю объект класса Ping, который перемещаю в созданный в IpStandardItemWithThread поток (moveToThread()) .
В классе Ping имеется функция, в которой в бесконечном цикле происходит пингование ip-адреса.

Возникли 2 проблемы:

Во-первых, при таком коннекте connect(itemPing,&Ping::isPing,[this](QString,bool statusPing){}
Вываливаются вот такие ошибки.

Код:
QObject::connect: Cannot queue arguments of type 'QVector<int>'
(Make sure 'QVector<int>' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QVector<int>'
(Make sure 'QVector<int>' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QVector<int>'
(Make sure 'QVector<int>' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QVector<int>'
(Make sure 'QVector<int>' is registered using qRegisterMetaType().)

Как от них избавиться?
Во-вторых, как по дестрою грамотно завершить поток и удалить объекты классов QThread  и Ping? При нынешнем варианте после клика на другой item вылетает программа, хотя должен завершаться поток и должна очищаться память, выделенная под объекты.

Класс пинга:

Код:
#ifndef PING_H
#define PING_H

#include "winsock2.h"
#include "iphlpapi.h"
#include "icmpapi.h"

#include <QObject>
#include <QString>

class Ping : public QObject
{
    Q_OBJECT
private:

    HANDLE hIcmpFile;                       // Обработчик
    unsigned long ipaddr;     // Адрес назначения
    DWORD dwRetVal;                     // Количество ответов
    char SendData[32];       // Буффер отсылаемых данных
    LPVOID ReplyBuffer;              // Буффер ответов
    DWORD ReplySize;

    QString _ipAddress;
    int _pingInterval;
public:
    explicit Ping(QString ipAddress,int pingInterval,QObject *parent = 0);
    ~Ping();

signals:

    void isPing(QString ipAddress,bool is_p);

public slots:
    void windowsPing();
};

#endif // PING_H

#include "ping.h"
#include <QDebug>


Ping::Ping(QString ipAddress,int pingInterval,QObject *parent) :
    QObject(parent),
    _pingInterval(pingInterval),
    _ipAddress(ipAddress),
    ipaddr(INADDR_NONE),
    dwRetVal(0),
    //SendData("Data Buffer"),
    ReplyBuffer(NULL),
    ReplySize(0)
{
    strcpy(SendData,"Data Buffer");


    // Установка ip-адреса

    ipaddr = inet_addr(_ipAddress.toStdString().c_str());
    hIcmpFile = IcmpCreateFile(); // создание обработчика

    // Выделение памяти под буффер ответов

    ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
    ReplyBuffer = (VOID*) malloc(ReplySize);



}

Ping::~Ping()
{
    if(ReplyBuffer) free(ReplyBuffer);
}



void Ping::windowsPing()
{
    while(true)
    {
        // Вызов функции ICMP эхо запроса
        dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 100);
        emit isPing(_ipAddress,(dwRetVal != 0));
        Sleep(_pingInterval);
    }
}

Класс модели таблицы:
Код:
#ifndef SEGMENTMODEL_H
#define SEGMENTMODEL_H

#include <QObject>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QModelIndex>
#include <QStringList>
#include <QIcon>
#include <QThread>


#include "centralmodel.h"
#include "ping.h"

class SegmentModel;
class IpStandardItemWithThread;

class SegmentModel : public QStandardItemModel
{
    Q_OBJECT
public:
    SegmentModel(QObject *parent = NULL);
private:
    QModelIndex _segmentModel;
    QString _segmentAddress;
    QString _segmentName;

    void insertRow(HostInfo ipInf);

public slots:
    void setNewSegment(QModelIndex segmentModel);
    void clearAll();
};



class IpStandardItemWithThread : public QStandardItem, public QObject
{
public:
    IpStandardItemWithThread(QString ipAddress,int pingInterval);
    ~IpStandardItemWithThread();
private:
    QString _ipAddress;
    int _pingInterval;
    QThread *ipThread;
    Ping *itemPing;
};



#endif // SEGMENTMODEL_H

#include "segmentmodel.h"

SegmentModel::SegmentModel(QObject *parent):
    QStandardItemModel(parent)
{
    this->setHorizontalHeaderLabels(QStringList() << tr("Д") << tr("IP") << tr("NetBIOS") << tr("MAC") << tr("Тип устройства") << tr("Операционная\nсистема") << tr("Комментарий") << tr("Р"));
}

void SegmentModel::insertRow(HostInfo ipInf)
{

    IpStandardItemWithThread *item0 = new IpStandardItemWithThread(ipInf.IpAddress,3000);
    item0->setEditable(false);
    item0->setSelectable(false);

    QStandardItem *item1 = new QStandardItem(ipInf.IpAddress);
    item1->setEditable(false);
    item1->setSelectable(false);

    QStandardItem *item2 = new QStandardItem(ipInf.NetBIOS);
    item2->setEditable(false);
    item2->setSelectable(false);

    QStandardItem *item3 = new QStandardItem(ipInf.MAC);
    item3->setEditable(false);
    item3->setSelectable(false);

    QStandardItem *item4 = new QStandardItem(ipInf.DeviceType);
    item4->setEditable(false);
    item4->setSelectable(false);

    QStandardItem *item5 = new QStandardItem(ipInf.OS);
    item5->setEditable(false);
    item5->setSelectable(false);

    QStandardItem *item6 = new QStandardItem(ipInf.comment);
    item6->setEditable(false);
    item6->setSelectable(false);

    QStandardItem *item7 = new QStandardItem("");
    item7->setEditable(false);
    item7->setSelectable(false);

    this->appendRow(QList<QStandardItem *>() << item0 << item1 << item2 << item3 << item4 << item5 << item6 << item7);
}

void SegmentModel::setNewSegment(QModelIndex segmentModel)
{
    _segmentModel = segmentModel;
    _segmentAddress = _segmentModel.data().toString();
    _segmentName = _segmentModel.sibling(_segmentModel.row(),1).data().toString();


    QStandardItem *segmentItem = ((CentralModel*)(_segmentModel.model()))->itemFromIndex(_segmentModel);


    for(int i=0;i<segmentItem->rowCount();i++)
    {
        IpStandardItem *ipItem = (IpStandardItem *)segmentItem->child(i,0);
        insertRow(ipItem->getIpInformation());
    }



}

void SegmentModel::clearAll()
{
    this->removeRows(0,this->rowCount());
}

IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval):
    _ipAddress(ipAddress),
    _pingInterval(pingInterval)
{
    this->setIcon(QIcon(":/icons/ping_unknown.png"));

    ipThread = new QThread();

    itemPing = new Ping(_ipAddress,_pingInterval);

    itemPing->moveToThread(ipThread);


    connect(itemPing,&Ping::isPing,[this](QString,bool statusPing){
        if(statusPing)
        {
            this->setIcon(QIcon(":/icons/ping_on.png"));
        }
        else
        {
            this->setIcon(QIcon(":/icons/ping_off.png"));
        }
    });

    connect(ipThread,SIGNAL(started()),itemPing,SLOT(windowsPing()));

    connect(ipThread,SIGNAL(finished()),itemPing,SLOT(deleteLater()));

    ipThread->start();
}

IpStandardItemWithThread::~IpStandardItemWithThread()
{

    if(ipThread->isRunning())
    {
        ipThread->terminate();

    }
}
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1 : Ноябрь 13, 2017, 13:11 »

А не проще ли юзать QProcess && утилиту ping?
Записан

ArchLinux x86_64 / Win10 64 bit
Павелъ
Гость
« Ответ #2 : Ноябрь 13, 2017, 15:05 »

А не проще ли юзать QProcess && утилиту ping?

А как это поможет в решении моей проблемы?
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #3 : Ноябрь 13, 2017, 15:24 »

А как это поможет в решении моей проблемы?
Товарищ kuzulis предлагает использовать процесс вместо потока. А за освобождение ресурсов процесса заботится уже ОС.
Записан
Павелъ
Гость
« Ответ #4 : Ноябрь 13, 2017, 16:32 »

Не подходит этот вариант из-за того, что системный пинг (то бишь утилита ping) требует определённое время для своей работы.

Через winAPI делается это очень быстро.

Я уже не знаю. Начитался супер "мудрых" статей о том, что согласно ООП не кошерно наследовать QThread и переопределять run(). Сделал по их статьям movetothread(). А как грамотно завершить поток так никто нигде не может сказать.

Неужели такая жесть с этими потоками?

Как его просто завершить?

Я уже вместо true использую bool переменную в while, чтобы выйти из цикла.

И всё равно thread.isRunning().

Как его завершить-то?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Ноябрь 13, 2017, 17:00 »

Я уже вместо true использую bool переменную в while, чтобы выйти из цикла.
Способ правильный и фактически единственный. Нитка должна сама завершиться, вынудить ее - себе дороже
И всё равно thread.isRunning().
Значит что-то не так. Остановитесь в отладчике и посмотрите что происходит
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #6 : Ноябрь 13, 2017, 17:13 »

Цитировать
требует определённое время для своей работы.

зависит от аргументов.. можно и навечно запустить
Записан

ArchLinux x86_64 / Win10 64 bit
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #7 : Ноябрь 13, 2017, 23:45 »

Код:
ping /?
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #8 : Ноябрь 14, 2017, 08:16 »

Через winAPI делается это очень быстро.

аватарку пингвина для отпугивания bsd демонов поставил ? )

а если через потоки, так тут qtconcurrent напрашивается
Записан
Павелъ
Гость
« Ответ #9 : Ноябрь 14, 2017, 11:21 »



аватарку пингвина для отпугивания bsd демонов поставил ? )


На работе ОС выбирать не приходится
Записан
Павелъ
Гость
« Ответ #10 : Ноябрь 14, 2017, 11:25 »

а если через потоки, так тут qtconcurrent напрашивается

Есть простейший пример запуска QConcurent и завершения по сигналу или дестрою?


Допустим есть класс, в котором есть функция doWork
в ней

while(true)
{
ping;
emit status_ping();
}


Как это реализовать?
Записан
zhbr
Гость
« Ответ #11 : Ноябрь 14, 2017, 11:38 »


Во-первых, при таком коннекте connect(itemPing,&Ping::isPing,[this](QString,bool statusPing){}
Вываливаются вот такие ошибки.

Как от них избавиться?
чтонить типа `qRegisterMetaType<QVector<int>>("QVector<int>");` вставить кудато в начало программы (главное чтобы выполнилось до connect)

Во-вторых, как по дестрою грамотно завершить поток и удалить объекты классов QThread  и Ping?

по дестрою чего?

я бы запускал пинг через QtConcurent::run().

но если хочешь с QThread, то важно чтобы метод Ping::windowsPing() закончил выполнение (прервать выполнение цикла либо через условие в заголовке цикла, либо внутри break по условию), затем вызвать thread.quit() (можно в конце Ping::windowsPing() эмитировать сигнал, предварительно связав его с thread.quit()), ну и потом можно удалять объекты.

Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #12 : Ноябрь 14, 2017, 13:28 »

Как это реализовать?

Код:
QFutureWatcher<QByteArray>* fw = new QFutureWatcher<QByteArray>(this);

fw->setFuture(QtConcurrent::run ( [=]() {

// YOUR CODE HERE

QProcess p;
p.start("/bin/bash", QStringList() << "-c" << "ping -c 1 -w 2 8.8.8.8");
p.waitForFinished();
return p.readAll().simplified();
}));

connect(fw, &QFutureWatcher<QString>::finished, this, [=] {
qDebug() << fw->result();
});
Записан
Павелъ
Гость
« Ответ #13 : Ноябрь 14, 2017, 16:01 »

Как это реализовать?

Код:
QFutureWatcher<QByteArray>* fw = new QFutureWatcher<QByteArray>(this);

fw->setFuture(QtConcurrent::run ( [=]() {

// YOUR CODE HERE

QProcess p;
p.start("/bin/bash", QStringList() << "-c" << "ping -c 1 -w 2 8.8.8.8");
p.waitForFinished();
return p.readAll().simplified();
}));

connect(fw, &QFutureWatcher<QString>::finished, this, [=] {
qDebug() << fw->result();
});

Вынуждаете использовать системный пинг)))
Ну, ладно, буду его использовать.

Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках.

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

В Линукс в этом плане лучше - вывод пинга всегда на английском.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #14 : Ноябрь 14, 2017, 16:24 »

Я в Windows столкнулся с такой проблемой, что в зависимости от языка ОС - вывод в терминале пинга - тоже на разных языках.

chcp 437 && ping -n 1 -w 2 8.8.8.8
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.057 секунд. Запросов: 22.