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

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

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

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

chcp 437 && ping -n 1 -w 2 8.8.8.8


А как запустить эти 2 процесса вместе в QProcess? Не получается.
Записан
Павелъ
Гость
« Ответ #16 : Ноябрь 15, 2017, 10:51 »

Сделал процесс в QConcurrent::run().

Однако, при удалении моего объекта класса, наследуемого от QStandardItem (в котором всё и происходит), вылетает программа.
Ошибки связаны с памятью.

ASSERT: "pid" in file io\qprocess_win.cpp, line 771
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.

QWaitCondition: Destroyed while threads are still waiting
QWaitCondition: Destroyed while threads are still waiting
QWaitCondition: Destroyed while threads are still waiting
QWaitCondition: Destroyed while threads are still waiting

Вот мой участок кода, отвечающий за это.


Код:
IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval):
    _ipAddress(ipAddress),
    _pingInterval(pingInterval),
    unknownIcon(QIcon(":/icons/ping_unknown.png")),
    onIcon(QIcon(":/icons/ping_on.png")),
    offIcon(QIcon(":/icons/ping_off.png"))
{
    this->setIcon(unknownIcon);

    QFutureWatcher<QByteArray>* fw = new QFutureWatcher<QByteArray>(this);

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



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

        // YOUR CODE HERE

        QProcess p;

        connect(this,&IpStandardItemWithThread::closeProcess,[this,&p](){
            if(p.isOpen())
            {
                p.kill();
                p.waitForFinished();

                qDebug() << p.isOpen();
            }
        });


        p.start("C:/Windows/System32/PING.EXE",QStringList() << "-n" << "1" << "-w" << "2" << _ipAddress);




        qApp->processEvents();
        p.waitForFinished();
        qApp->processEvents();


            return p.readAll().simplified();
    }));

}

IpStandardItemWithThread::~IpStandardItemWithThread()
{
    //itemPing->setWorkFalse();

    emit closeProcess();
}


Суть в чём.
Ещё раз повторюсь.
Есть объект QTableView.
Каждый раз при клике на айтем второго уровня в QTreeView у меня создаются айтемы для QTableView. При клике на другой айтем в QTreeView – вызываю функцию clearAll, которая делает removerows для модели QTableView. То, есть удаляет все айтемы в QTableView.

Один из айтемов в модели QTableView я изменил.
Сделал наследование от QStandardItem, код этого изменённого класса представлен выше.

То, есть в айтеме у меня создаётся процесс, который автоматически должен удаляться при удалении айтема в деструкторе. Но, программа вылетает.

Собственно, что мне надо. Чтобы происходил пинг для каждого ip-адреса в таблице и  прекращался при закрытии таблицы. А после прекращения, чтобы очищалась память, выделенная под пинги в айтеме.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


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

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

chcp 437 && ping -n 1 -w 2 8.8.8.8


А как запустить эти 2 процесса вместе в QProcess? Не получается.

это не два процесса, а один - это команда для cmd.exe /с
Записан
Павелъ
Гость
« Ответ #18 : Ноябрь 15, 2017, 14:54 »

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

chcp 437 && ping -n 1 -w 2 8.8.8.8


А как запустить эти 2 процесса вместе в QProcess? Не получается.

это не два процесса, а один - это команда для cmd.exe /с


Так

Код:
proc->start("C:/Windows/System32/CMD.EXE",QStringList() << "/c" << "C:/Windows/System32/CHCP.COM" << "437" << "&&" << "C:/Windows/System32/PING.EXE" << "-n" << "1" << "-w" << "2" <<  _ipAddress);

не работает.

Так
Код:
proc->start("C:/Windows/System32/CMD.EXE",QStringList() << "/c" << "C:/Windows/System32/CHCP.COM 437" << "&&" << "C:/Windows/System32/PING.EXE -n 1 -w 2 " +  _ipAddress);

тоже не работает.

Для одного пинга работает:
Код:
proc->start("C:/Windows/System32/PING.EXE",QStringList() << "-n" << "1" << "-w" << "2" << _ipAddress);
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


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

process.start("cmd", QStringList() << "/C" << "chcp 437 && ping -n 1 -w 2 8.8.8.8");
Записан
Павелъ
Гость
« Ответ #20 : Ноябрь 15, 2017, 21:03 »

process.start("cmd", QStringList() << "/C" << "chcp 437 && ping -n 1 -w 2 8.8.8.8");

Да, это работает.

Только не завершается процесс при уничтожении айтема, из-за этого вылетает программа.
Записан
zhbr
Гость
« Ответ #21 : Ноябрь 16, 2017, 07:27 »


Код:
        qApp->processEvents();
        p.waitForFinished();
        qApp->processEvents();


processEvents тут не нужен, так как внутри потока запущенного через QtConcurent::run нет ни живущих объектов ни цикла обработки событий.
Записан
Павелъ
Гость
« Ответ #22 : Ноябрь 16, 2017, 15:40 »


Код:
        qApp->processEvents();
        p.waitForFinished();
        qApp->processEvents();


processEvents тут не нужен, так как внутри потока запущенного через QtConcurent::run нет ни живущих объектов ни цикла обработки событий.

спасибо
Записан
Павелъ
Гость
« Ответ #23 : Ноябрь 16, 2017, 15:41 »

Кажется, я понял, в чём дело.

Я создаю цикл в QConcurrent::run() и в условиях цикла пишу переменную, по которой собираюсь выйти из цикла.
Но, суть в том, что если я изменяю значение переменной за пределами QConcurrent::run(), в самом QConcurrent::run() она остаётся всё время одинаковой.
Как будто первый раз при создании QConcurrent::run() туда передались копии всех внешних переменных.
Я и по указателю пытался передавать переменную work, и писал функцию возвращающую текущее значение work, всё бесполезно – внутри переменные всегда одинаковые.

В связи с чем возникли 2 новых вопроса.
1)   Как сделать, чтобы в  QConcurrent::run() переменные ОБНОВЛЯЛИСЬ? А то у меня подозрения, что из-за этого и пинг некорректно определяется.
2)   Как замедлить выполнение в цикле while внутри QConcurrent::run(), чтобы скажем раз в 500 мс выполнялось тело цикла? Никаких sleep() внутри QStandardItem нет.
Спасибо.

Код:
class IpStandardItemWithThread : public QStandardItem
{
public:
    IpStandardItemWithThread(QString ipAddress,int pingInterval);
    ~IpStandardItemWithThread();
private:
    QString _ipAddress;
    int _pingInterval;
    QIcon unknownIcon, onIcon,offIcon;

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

    QFuture<void> future;

    bool work;
};



Код:
IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval):
    _ipAddress(ipAddress),
    _pingInterval(pingInterval),
    unknownIcon(QIcon(":/icons/ping_unknown.png")),
    onIcon(QIcon(":/icons/ping_on.png")),
    offIcon(QIcon(":/icons/ping_off.png")),
    ipaddr(INADDR_NONE),
    ReplyBuffer(NULL),
    ReplySize(0),
    work(true)
{

    this->setIcon(unknownIcon);


    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);


    future = QtConcurrent::run([this](){

        while(work)
        {
            if(IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 100))
            {
                this->setIcon(onIcon);
                qDebug() << _ipAddress << work;
            }
            else
            {
                this->setIcon(offIcon);
                qDebug() << _ipAddress << work;
            }
        }
    });

}

IpStandardItemWithThread::~IpStandardItemWithThread()
{
    work = false;
    future.waitForFinished();

    if(ReplyBuffer)
    {
        free(ReplyBuffer);
        ReplyBuffer = NULL;
    }
}

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

Сообщений: 1175


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

1. лучше взять тогда поток, если хочется между ними бросать изменения переменных
2. тогда и замедление не нужно - будут события
Записан
Павелъ
Гость
« Ответ #25 : Ноябрь 16, 2017, 17:07 »

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

А насколько корректным является то, что я сделал теперь?

Я взял таймер и засунул в него QFuture. Форма не виснит.

Код:
#include "segmentmodel.h"
#include "segmentwidget.h"


SegmentModel::SegmentModel()
{
    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,1000);
    item0->setEditable(false);
    item0->setSelectable(false);


    connect(item0,SIGNAL(canRepaint()),this,SIGNAL(canRepaint()));

    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::insertClearRow()
{
    if(this->rowCount() < 254)
    {
        this->setRowCount(this->rowCount() + 1);
    }
}


IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval):
    _ipAddress(ipAddress),
    _pingInterval(pingInterval),
    unknownIcon(QIcon(":/icons/ping_unknown.png")),
    onIcon(QIcon(":/icons/ping_on.png")),
    offIcon(QIcon(":/icons/ping_off.png")),
    ipaddr(INADDR_NONE),
    ReplyBuffer(NULL),
    ReplySize(0),
    testTimer(new QTimer())
{

    this->setIcon(unknownIcon);


    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);




    connect(testTimer,&QTimer::timeout,[this](){

        future = QtConcurrent::run(this,testFunction);

    });


    testTimer->start(_pingInterval);

   

}

IpStandardItemWithThread::~IpStandardItemWithThread()
{
    testTimer->stop();

    future.waitForFinished();

    if(ReplyBuffer)
    {
        free(ReplyBuffer);
        ReplyBuffer = NULL;
    }

    if(testTimer)
    {
        delete testTimer;
        testTimer = NULL;
    }
}

void IpStandardItemWithThread::testFunction()
{
    if(IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 100))
    {
        this->setIcon(onIcon);
    }
    else
    {
        this->setIcon(offIcon);
    }

    emit canRepaint();
}



Только есть небольшая проблемка. Перерисовка иконок периодически в айтемах не происходит. Никаких методов типа repaint в QStandardItem нет.
Когда кликаю мышкой на айтем, то идёт перерисовка.
Я даже сделал высулку сигнала после добавления иконки. Но, дело в том, что таких айтемов одновременно может быть 254.
Перерисовка всего QTreeView не хорошее дело. Всё зависает.

Как можно обновить отображение айтема с иконкой?
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


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

Много говно-говно-кода, и, это все напоминает попытку наговнокодить еще больше, чтобы уже переливалось и "втереть нам какую-то дичь" (с). Нет желания ну никакого копаться в коде.
Записан

ArchLinux x86_64 / Win10 64 bit
Павелъ
Гость
« Ответ #27 : Ноябрь 16, 2017, 21:26 »

Много говно-говно-кода, и, это все напоминает попытку наговнокодить еще больше, чтобы уже переливалось и "втереть нам какую-то дичь" (с). Нет желания ну никакого копаться в коде.

тут говнокод только winAPIшный сишный участок, отвечающий за пинг, который я урвал в сети, и не знаю, что там происходит.
Что так вызвало отвращение, malloc что ли?
Я действовал по-принципу: работает - не трожь.

Или эта инициализация в конструкторе дико смотрится?

Код:
    _ipAddress(ipAddress),
    _pingInterval(pingInterval),
    unknownIcon(QIcon(":/icons/ping_unknown.png")),
    onIcon(QIcon(":/icons/ping_on.png")),
    offIcon(QIcon(":/icons/ping_off.png")),
    ipaddr(INADDR_NONE),
    ReplyBuffer(NULL),
    ReplySize(0),
    testTimer(new QTimer())

А так, всё чисто Qtшное.

Да, и не в этом дело.

Вопрос был в том, что если я в QTimer вызываю QConcurrent::run() - так нормально?

Получается, что всё чистится и закрывается.

И это не самое плохое решение на мой взгляд.

Если с QProcess мутить, то вывод его обрабатывается дольше, да и с неудачным завершением программы потом приходится в диспетчере задач удалять cmd.exe, ping.exe, chcp.com.

Завтра, как вариант, попробую repaint QTableView по таймеру сделать, чтобы всё-таки иконки в айтемах обновлялись не только при клике мышкой.
Записан
Павелъ
Гость
« Ответ #28 : Ноябрь 22, 2017, 13:38 »

Давно не было меня здесь.

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

Проблему я всё-таки решил с помощью QThread.
Переменную в цикле while, по которой необходимо выходить, объявил как volatile bool.

Использовал также QMutex.

Выкладываю участок своего говнокода, может кому-то понадобится сама идея. И да, всё-таки пришлось наследовать QThread и переопределять run(), хотя на Хабре этого делать не советуют.

Заголовочник
Код:
#ifndef SEGMENTMODEL_H
#define SEGMENTMODEL_H


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

#include <QThreadPool>


#include <QThread>

#include <QMutex>


#include "centralmodel.h"


class SegmentModel;
class IpStandardItemWithThread;
class PingThread;

class SegmentModel : public QStandardItemModel
{
    Q_OBJECT
public:
    SegmentModel(QObject *parent = nullptr);

    QString getSegmentAddress();

private:
    QModelIndex _segmentModel;
    QString _segmentAddress;
    QString _segmentName;


public slots:
    void setNewSegment(QModelIndex segmentModel);
    void insertRow(HostInfo ipInf);
    void removeIp(QString);

    void clearAll();
};



class IpStandardItemWithThread : public QObject, public QStandardItem
{
    Q_OBJECT
public:
    IpStandardItemWithThread(QString ipAddress,int pingInterval);
    ~IpStandardItemWithThread();
private:
    PingThread *_pThread;

    QIcon unknownIcon, onIcon,offIcon;
private slots:
    void setItemIcon(int);

};



class PingThread : public QThread
{
    Q_OBJECT

public:
    PingThread(QString ipAd,int ipInterval,QObject * parent = nullptr);
private:
    int _pingInterval;
    QString _ipAddress;
    volatile bool _stopped;
    QMutex mutex;
    void run();
signals:
    void statusPing(int);
public slots:
    void stop();
};




#endif // SEGMENTMODEL_H


сипипишник

Код:
#include "segmentmodel.h"
#include "segmentwidget.h"


SegmentModel::SegmentModel(QObject *parent):
    QStandardItemModel(parent)
{
}

QString SegmentModel::getSegmentAddress()
{
    return _segmentAddress;
}

void SegmentModel::insertRow(HostInfo ipInf)

    if(CommonFunctions::getSegmentFromIp(ipInf.IpAddress) != _segmentAddress) return;

    this->appendRow(QList<QStandardItem *>()
                    << new IpStandardItemWithThread(ipInf.IpAddress,1000)
                    << new QStandardItem(ipInf.IpAddress)
                    << new QStandardItem(ipInf.NetBIOS)
                    << new QStandardItem(ipInf.MAC)
                    << new QStandardItem(ipInf.DeviceType)
                    << new QStandardItem(ipInf.OS)
                    << new QStandardItem(ipInf.comment)
                    << new QStandardItem(""));

    for(int i=0;i<this->columnCount();i++)
        this->item(this->rowCount() - 1,i)->setEditable(false);

}

void SegmentModel::removeIp(QString ipAddr)
{
    if(CommonFunctions::getSegmentFromIp(ipAddr) != _segmentAddress) return;
    for(int i=0;i<this->rowCount();i++)
    {
        if(this->item(i,1)->text() == ipAddr)
        {
            this->removeRow(i);
            return;
        }
    }
}

void SegmentModel::clearAll()
{
    this->clear();
    _segmentName = "";
}

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

    _segmentModel = segmentModel;


    _segmentAddress = _segmentModel.data().toString();

    _segmentName = _segmentModel.sibling(_segmentModel.row(),1).data().toString();



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

    int i;

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

IpStandardItemWithThread::IpStandardItemWithThread(QString ipAddress, int pingInterval):
    unknownIcon(QIcon(":/icons/ping_unknown.png")),
    onIcon(QIcon(":/icons/ping_on.png")),
    offIcon(QIcon(":/icons/ping_off.png"))

{
    this->setIcon(unknownIcon);

    _pThread = new PingThread(ipAddress,pingInterval,this);


    connect(_pThread,SIGNAL(statusPing(int)),this,SLOT(setItemIcon(int)));

    _pThread->start();

}

IpStandardItemWithThread::~IpStandardItemWithThread()
{
    if(_pThread->isRunning())
    {
        _pThread->stop();

        _pThread->terminate();

        _pThread->wait();

    }
}

void IpStandardItemWithThread::setItemIcon(int pStatus)
{
    if(pStatus)
    {
        this->setIcon(onIcon);
    }
    else
    {
        this->setIcon(offIcon);
    }
}

PingThread::PingThread(QString ipAd, int ipInterval, QObject *parent):
    QThread(parent),
    _pingInterval(ipInterval),
    _ipAddress(ipAd),
    _stopped(false)
{

}


void PingThread::run()
{
    for(;;)
    {
        mutex.lock();

        if(_stopped)
        {
            _stopped = false;
            mutex.unlock();
            break;
        }
        mutex.unlock();

        msleep(_pingInterval);

        emit statusPing(CommonFunctions::isPingActive(_ipAddress));
    }
}

void PingThread::stop()
{
    mutex.lock();
    _stopped = true;
    mutex.unlock();
}

Записан
Страниц: 1 [2]   Вверх
  Печать  
 
Перейти в:  


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