Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблема с правильным завершением дополнительного потока
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
AD
Я не понимаю, почему у меня нет корректного завершения потока. Заходит в поток только при первом вызове, а соответственно рисует график только при первом выборе параметров, далее необходимо закрывать это диалоговое окошко и открывать снова- что крайне неудобно. Просьба, пожалуйста, поясните, в чем ошибки?
Ниже приведен код:
thread

class GraphicDisplay : public QDialog, public Ui::GraphicDisplayClass
{
    Q_OBJECT

private:
    QMap<int, QVector<QPointF>> curveMap;       ///< список всех изображаемых кривых
    // куча переменных

private:
    void drawGrid(QPainter* painter);
    void drawCurves(QPainter* painter);
    void drawLegend(QPainter* painter, QRect& rect, int& k, int id);
    QPointF initXY(double& sx, double& sy);
    QPointF initXY(const QPoint* scr_coor);

protected:
    void paintEvent(QPaintEvent* events);
    void keyPressEvent(QKeyEvent* events);
    void wheelEvent(QWheelEvent* events);
    void mousePressEvent(QMouseEvent* events);
    void mouseMoveEvent(QMouseEvent* events);
    void mouseReleaseEvent(QMouseEvent* events);
    void contextMenuEvent(QContextMenuEvent* events);
    void resizeEvent(QResizeEvent* events) { QDialog::resizeEvent(events); update(); }
    void closeEvent(QCloseEvent* events) { QDialog::closeEvent(events); }
    void showEvent(QShowEvent* events) { QDialog::showEvent(events); }

public:
    GraphicDisplay(QWidget *parent = 0);
    GraphicDisplay(QWidget *parent, ParamPlotSettings& st);
    ~GraphicDisplay();
    // куча функций
    void initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts);
                void fillCurve();
};

/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts)
{
    p_thread -> mainWindow = mainWindow;
    p_thread -> pDist = pD;
    p_thread -> pTime = pT;
    p_thread -> settings = sts;
    p_thread -> CurveMap(&curveMap);
}

/// Заполнение вектора данными
void GraphicDisplay::fillCurve()
{
    curveMap.clear();
    p_thread -> start();
    if(p_thread -> getStop())
        p_thread -> stop();

    update();
}


/// Класс параллельного потока для заполнения данными кривых графика
class DTThread: public QThread
{
    Q_OBJECT

private:
    QMap<int, QVector<QPointF>>* curveMap;      ///< список изображаемых кривых
    volatile bool stopped;                        ///< переменная, контролирующая начало и конец выполнения потока
    QMutex mtx;                                    ///< мьютекс для блокировки данных при работе с дополнительным потоком

public:
    QPen myPen;                                    ///< карандаш для рисования линий определенной жирности и цвета
    TLV* mainWindow;                            ///< указатель на главное окно
    QVector<QPointF> data;                        ///< вектор загружаемой кривой
    QDialDistParam* pDist;                        ///< указатель на класс диалога параметров по расстоянию
    QDialTimeParam* pTime;                        ///< указатель на класс диалога параметров по времени
    PlotSettings* settings;                        ///< указатель на настройку для определения масштаба

private:
    void fillXVec(QVector<QPointF>& data);

protected:
    void run();

public:
    DTThread(): stopped(false), pDist(0), pTime(0), mainWindow(0) {}
    ~DTThread() { mtx.lock(); stopped = true; mtx.unlock(); }
    void stop() { mtx.lock(); stopped = true; mtx.unlock(); quit(); };
    QMap<int, QVector<QPointF>>* CurveMap() { return curveMap; }
    void CurveMap(QMap<int, QVector<QPointF>>* crv) { mtx.lock();  curveMap = crv; mtx.unlock(); }
    bool getStop() { return stopped; }
};

/// Запуск дополнительного потока
void DTThread::run()
{
    if(!stopped)
    {
        int id = 0;
        /// Заполнение данными по оси x
        fillXVec(data);

        for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
        {
            /// Заполнение данными по оси y
            ParamDescr* param_record = fit -> param_record;
            int index = 0;
            for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
            {
                PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
                                                    param_record -> Address());
                data[index].setY(val.value);
            }
            curveMap -> insert(id, data);
        }
        mtx.lock(); stopped = true; mtx.unlock();
    }

    exec();
}

/// Заполнение данными по оси x
void DTThread::fillXVec(QVector<QPointF>& data)
{
    double x = 0.0;
    ParamPlotSettings* p_set = (ParamPlotSettings*)settings;
    switch(p_set -> win_type)
    {
    case DISTPARAM:
        for(QVector<PARAMVALUE>::iterator iter=x_data.dist_x.begin(); iter!=x_data.dist_x.end(); ++iter)
        {
            x += (iter -> value / 1000.0);
            data.push_back(QPointF(x, 0.0));
            int k = 0;
            switch(iter -> status)
            {
            case PS_FAIL:
                k = pDist -> spinFault -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_NODATA:
                k = pDist -> spinNoData -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_TEST:
                k = pDist -> spinTest -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_OK:
                k = pDist -> spinNormal -> text().toInt();
                myPen.setWidth(k);
            break;
            }
        }
    break;
    case TIMEPARAM:
        for(QVector<PARAMVALUE>::iterator iter=x_data.time_x.begin(); iter!=x_data.time_x.end(); ++iter)
        {
            x += (iter -> value / 60.0);
            data.push_back(QPointF(x, 0.0));
            int k = 0;
            switch(iter -> status)
            {
            case PS_FAIL:
                k = pTime -> spinFault -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_NODATA:
                k = pTime -> spinNoData -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_TEST:
                k = pTime -> spinTest -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_OK:
                k = pTime -> spinNormal -> text().toInt();
                myPen.setWidth(k);
            break;
            }
        }
    break;
    }
}



Заранее благодарен за помощь!
ЙаМайскЫйПчОЛ
Удалено
Tonal
Всё правильно - так и должно быть (в смысле выполнятся 1 раз).
В коде нужно if заменить на while, stopped с bool на QWaitCondition
и добавить ещё QWaitCondition для убиения или лучше на QThreadPool или QtConcurrent::run переделать - тогда и стопать не надо и специально завершать. :)

П.С. Ты уверен, что тут нужен отдельный поток?
П.П.С. Убей дубль сообщения, чтобы не смущать народ. :)
AD
Цитата(Tonal)
Всё правильно - так и должно быть (в смысле выполнятся 1 раз).
В коде нужно if заменить на while, stopped с bool на QWaitCondition
и добавить ещё QWaitCondition для убиения или лучше на QThreadPool или QtConcurrent::run переделать - тогда и стопать не надо и специально завершать. :)

П.С. Ты уверен, что тут нужен отдельный поток?
П.П.С. Убей дубль сообщения, чтобы не смущать народ. :)

С РАДОСТЬЮ УБИЛ БЫ, но у меня нет таких прав, а там уже тоже понаписали, блин!
Был бы рад, если бы Вы набросок сделали, так не очень понятно. Можно с помощью QThreadPool набросок?

P.S. Юра, ты убил? :) Спасибо!

Стоп: в Qt 4.3.2 нет ни QThreadPool, ни QtConcurrent!!!

Возможно я опять криво выразился, но мне необходимо, чтобы run запускался каждый раз, после нажатия кнопочки ОК! Чтобы поняли, о чем я, смотрите тему трепа "Результаты проекта", последние 2 рисунка.
Tonal
Переходи на 4.4? :)

А набросок примерно такой:
QWaitCondition w_terminated;
QMutex m_terminated;
QReadWriteLock rw_run;
void DTThread::Run() {
  while(!w_terminated.wait(m_terminated, 0)) {
    QWriteLocker w_run(rw_run);
    //Здесь выполняем то, что у тебя было внутри if(!stopped)
  }
}

/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts) {
  //Здесь старая инициализация
    w_run.lockForRead();
    p_thread -> start();
}

/// Заполнение вектора данными
void GraphicDisplay::fillCurve() {
    curveMap.clear();
    w_run.unlock();
    w_run.lockForRead();
    update();
}

Ну и в конце приложения нужно w_terminated.wakeAll() сделать.
Только смысла в этом всём нет, т.к. у тебя получается, что главный поток просто замерзает и ждёт окончания рабочего.
Чтобы не замерзал, нужно fillCurve разбить ровно на 2 половины: 1 - начать пересчёт, 2 - проверить не посчиталось ли (tryLockForRead), и если посчиталось - обновится. :)
Ну и данные и потока и главного окна должны быть разные в этом случае.
AD
Цитата(Tonal @ 12.9.2008, 12:29) *
Переходи на 4.4? :)


За набросок спасибо, буду разбираться.

Перейти на Qt 4.4 не могу. Коммерческая версия с интеграцией в Visual Studio 2005!

Цитата
Ну и в конце приложения нужно w_terminated.wakeAll() сделать.
Только смысла в этом всём нет, т.к. у тебя получается, что главный поток просто замерзает и ждёт окончания рабочего.
Чтобы не замерзал, нужно fillCurve разбить ровно на 2 половины: 1 - начать пересчёт, 2 - проверить не посчиталось ли (tryLockForRead), и если посчиталось - обновится.
Ну и данные и потока и главного окна должны быть разные в этом случае.

НЕ, не хочу, чтобы все застывало. В итоге я сделать хочу именно так, чтобы он заполнял по-частям. Но а пока сделать версию, чтобы это заполнение целиком происходило в доп. потоке! То, что работает быстрее, уже проверил! Т.е. приведенный кусок использовать без всяких вэйт кондишен? Мьютекс я использовал уж так, для того, чтобы не забыть, что я планирую в итоге сделать! А щас, хочу, чтобы попросту, после того, как в потоке заполнилось все, оно сразу же отобразилось!
Tonal
Я же написал, что нужно изменить - fillCurve() разбить на 2:
/// Заполнение вектора данными
void GraphicDisplay::fillCurve() {
    p_thread ->clearCurveMap();
    w_run.unlock();
}
void GraphicDisplay::test4Update()
    if (w_run.tryLockForRead()) {
      curveMap = p_thread->getCurveMap();
      update();
    }
}

Причём эту test4Update() дёргать или в идле, или по таймеру, пока не посчитается.
Ну и данные curveMap сделать разные для основного потока и для рабочего. Иначе всё равно придётся ждать окончания генерации.
AD
Ну и последний вопросик:
А набросок примерно такой:
QWaitCondition w_terminated;QMutex m_terminated;QReadWriteLock rw_run;void DTThread::Run()
{  
while(!w_terminated.wait(m_terminated, 0))
{    
         QWriteLocker w_run(rw_run);  
          //Здесь выполняем то, что у тебя было внутри if(!stopped)  
}
}

Опечатка? Имелось в виду w_run.writeForLock()? У меня есть rw_run, который в классе объявлен, а w_run вообще локально.
AD
Видимо, где-то напортачил, но вот где? Ломается с сообщением:
Цитата
mutex must be unlocked in the same thread, that locked!

Вот код:
thread
/// Класс параллельного потока для заполнения данными кривых графика
class DTThread: public QThread
{
    Q_OBJECT

private:
    QMap<int, QVector<QPointF>> curveMap;      ///< список изображаемых кривых

public:
    QPen myPen;                                    ///< карандаш для рисования линий определенной жирности и цвета
    TLV* mainWindow;                            ///< указатель на главное окно
    QVector<QPointF> data;                        ///< вектор загружаемой кривой
    QDialDistParam* pDist;                        ///< указатель на класс диалога параметров по расстоянию
    QDialTimeParam* pTime;                        ///< указатель на класс диалога параметров по времени
    PlotSettings* settings;                        ///< указатель на настройку для определения масштаба
    QMutex m_terminated;                        ///< мьютекс для блокировки данных при работе с дополнительным потоком
    QWaitCondition w_terminated;                ///< критическая секция для разблокировки мьютексов
    QReadWriteLock rw_run;                        ///< мьютекс для записи и чтения

private:
    void fillXVec(QVector<QPointF>& data);

protected:
    void run();

public:
    DTThread(): pDist(0), pTime(0), mainWindow(0) {}
    ~DTThread() {}
    QMap<int, QVector<QPointF>> CurveMap() { return curveMap; }
    void CurveMap(QMap<int, QVector<QPointF>> crv) { curveMap = crv; }
    void clearCurveMap() { curveMap.clear(); }
    void wake_all() { w_terminated.wakeAll(); }
};

/// Запуск дополнительного потока
void DTThread::run()
{
    while(!w_terminated.wait(&m_terminated, 0))
    {
        rw_run.lockForRead();
        int id = 0;
        /// Заполнение данными по оси x
        fillXVec(data);

        for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
        {
            /// Заполнение данными по оси y
            ParamDescr* param_record = fit -> param_record;
            int index = 0;
            for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
            {
                PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
                                                    param_record -> Address());
                data[index].setY(val.value);
            }
            curveMap.insert(id, data);
        }
    }

    exec();
}

/// Заполнение данными по оси x
void DTThread::fillXVec(QVector<QPointF>& data)
{
    double x = 0.0;
    ParamPlotSettings* p_set = (ParamPlotSettings*)settings;
    switch(p_set -> win_type)
    {
    case DISTPARAM:
        for(QVector<PARAMVALUE>::iterator iter=x_data.dist_x.begin(); iter!=x_data.dist_x.end(); ++iter)
        {
            x += (iter -> value / 1000.0);
            data.push_back(QPointF(x, 0.0));
            int k = 0;
            switch(iter -> status)
            {
            case PS_FAIL:
                k = pDist -> spinFault -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_NODATA:
                k = pDist -> spinNoData -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_TEST:
                k = pDist -> spinTest -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_OK:
                k = pDist -> spinNormal -> text().toInt();
                myPen.setWidth(k);
            break;
            }
        }
    break;
    case TIMEPARAM:
        for(QVector<PARAMVALUE>::iterator iter=x_data.time_x.begin(); iter!=x_data.time_x.end(); ++iter)
        {
            x += (iter -> value / 60.0);
            data.push_back(QPointF(x, 0.0));
            int k = 0;
            switch(iter -> status)
            {
            case PS_FAIL:
                k = pTime -> spinFault -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_NODATA:
                k = pTime -> spinNoData -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_TEST:
                k = pTime -> spinTest -> text().toInt();
                myPen.setWidth(k);
            break;
            case PS_OK:
                k = pTime -> spinNormal -> text().toInt();
                myPen.setWidth(k);
            break;
            }
        }
    break;
    }
}

/// Заполнение вектора данными
void GraphicDisplay::fillCurve()
{
    p_thread -> clearCurveMap();
    p_thread -> rw_run.unlock();
}

/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts)
{
    p_thread -> mainWindow = mainWindow;
    p_thread -> pDist = pD;
    p_thread -> pTime = pT;
    p_thread -> settings = sts;
    p_thread -> CurveMap(curveMap);

    p_thread -> rw_run.lockForRead();
    p_thread -> start();

    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(forUpdate()));
    timer -> start(UPDATETIME);
}

/// Обновление данных списка
void GraphicDisplay::forUpdate()
{
    if(p_thread -> rw_run.tryLockForRead())
    {
        curveMap = p_thread -> CurveMap();
        update();
    }
}

/// Заставить остановиться остальным процессам
void GraphicDisplay::wake_all()
{
    p_thread -> wake_all();
}



/// Нажатие на кнопку OK
void QDialDistParam::btnOKClick()
{
    ParamPlotSettings settings;
    settings.setMaxX(q_calc.maxDist, DISTPARAM);
    settings.setMinX(x_data.dist_x.front().value, DISTPARAM);
    double c_max = 10.0, c_min = 0.0;
    findMinMaxY(c_min, c_max);
    settings.setMinY(c_min);
    settings.setMaxY(c_max);
    settings.adjust();

    graphic -> setPlotSettings(settings);
    graphic -> pDist = this;
    graphic -> retranslateHeader(graphic);
    graphic -> initThread(this, 0, &settings);

    graphic -> fillCurve();
    graphic -> wake_all();
    graphic -> exec();
}
trdm
Цитата(Tonal @ 12.9.2008, 13:03) *
Причём эту test4Update() дёргать или в идле, или по таймеру, пока не посчитается.

а сигнал нельзя послать об окончании пересчета?
AD
Немного изменил. Все-равно ломается, во-первых, правда, в другом месте.
Подскажите, пожалуйста, где ошибка.
Правка произошла в функции run:
/// Запуск дополнительного потока
void DTThread::run()
{
    m_terminated.lock();
    while(!w_terminated.wait(&m_terminated, 0))
    {
        int id = 0;
        /// Заполнение данными по оси x
        fillXVec(data);

        for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
        {
            /// Заполнение данными по оси y
            ParamDescr* param_record = fit -> param_record;
            int index = 0;
            for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
            {
                PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
                                                    param_record -> Address());
                data[index].setY(val.value);
            }
            curveMap.insert(id, data);
        }
    }

    w_terminated.wakeAll();
    m_terminated.unlock();
    exec();
}

Соответственно w_termainated.wakeAll() перенесена в run()!
Я уже запутался, где ошибка.
AD
Поправил немного. Но все-равно, работает кривовато. Причем глюки очень разнообразные: то график отображается параметра, который выбран не в данный момент, а во время предыдущей выборке, то совсем не отображается, то застревает на этапе выборки. Может быть сможете подсказать, где копнуть, чтобы поправить это безобразие?
thread

GraphicDisplay::GraphicDisplay(QWidget *parent): QDialog(parent)
{
    p_thread = new DTThread;
}

GraphicDisplay::GraphicDisplay(QWidget *parent, ParamPlotSettings& st): QDialog(parent)
{
    p_thread = new DTThread;
}

GraphicDisplay::~GraphicDisplay()
{
    delete rubber;
    delete contextMenu;

    if(timer)
    {
        timer -> stop();
        delete timer;
    }
    if(p_thread)
    {
        p_thread -> quit();
        if(p_thread -> isRunning()) p_thread -> terminate();
        delete p_thread;
    }
}

/// Отрисовка графика
void GraphicDisplay::paintEvent(QPaintEvent* events)
{
    painter.begin(this);

    painter.setWindow(paramsDisplay -> rect());
    painter.setFont(QFont("Tahoma", 8, Qt::SolidLine));
    painter.setPen(Qt::black);

    if(gridAction -> isChecked()) drawGrid(&painter);
    drawCurves(&painter);

    if(timer != 0) if(curveMap.size() == fact_prm.size())
        timer -> stop();

    painter.end();
}

/// Заполнение вектора данными
void GraphicDisplay::fillCurve()
{
      p_thread -> clearCurveMap();
      p_thread -> rw_run.unlock();
}

/// Инициализация нужных для потока параметров
void GraphicDisplay::initThread(QDialDistParam* pD, QDialTimeParam* pT, PlotSettings* sts)
{
    p_thread -> mainWindow = mainWindow;
    p_thread -> pDist = pD;
    p_thread -> pTime = pT;
    p_thread -> settings = sts;
    p_thread -> CurveMap(curveMap);

    p_thread -> rw_run.lockForRead();
    p_thread -> start();

    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(forUpdate()));
    timer -> start(UPDATETIME);
}

/// Обновление данных списка
void GraphicDisplay::forUpdate()
{
    if(p_thread -> rw_run.tryLockForRead())
    {
        curveMap = p_thread -> CurveMap();
        update();
    }
}

/// Закрытие окна
void GraphicDisplay::closeEvent(QCloseEvent* events)
{
    QDialog::closeEvent(events);
    if(p_thread)
    {
        if(p_thread -> isRunning()) p_thread -> terminate();
        p_thread -> quit();
    }
    if(timer)
        timer -> stop();
}



/// Запуск дополнительного потока
void DTThread::run()
{
    m_terminated.lock();
    while(!w_terminated.wait(&m_terminated, 0))
    {
        int id = 0;
        /// Заполнение данными по оси x
        fillXVec(data);

        for(fact_param_iter fit=fact_prm.begin(); fit!=fact_prm.end(); ++fit, ++id)
        {
            m_terminated.unlock();
            /// Заполнение данными по оси y
            ParamDescr* param_record = fit -> param_record;
            int index = 0;
            for(logI jter=mainWindow -> log.begin(); jter!=mainWindow -> log.end() && index!=data.size(); ++jter, ++index)
            {
                PARAMVALUE val = jter -> GetParamValue(&mainWindow -> cur_rec, param_record -> Name(),
                                                    param_record -> Address());
                data[index].setY(val.value);
            }
            curveMap.insert(id, data);
            m_terminated.lock();
        }
    }

    m_terminated.unlock();
    exec();
}

Tonal
Извини, правда некогда разбираться. :(
Но, если ты таки хочешь работать с потоками, стоит самому разобраться - я же твою прогу писать не буду. :)
AD
Не знаю уж чего я там еще такого наваял, но пока вроде бы работает без багов! :)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.