crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> Проблема с правильным завершением дополнительного потока
AD
  опции профиля:
сообщение 12.9.2008, 10:06
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Я не понимаю, почему у меня нет корректного завершения потока. Заходит в поток только при первом вызове, а соответственно рисует график только при первом выборе параметров, далее необходимо закрывать это диалоговое окошко и открывать снова- что крайне неудобно. Просьба, пожалуйста, поясните, в чем ошибки?
Ниже приведен код:
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;
    }
}



Заранее благодарен за помощь!
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ЙаМайскЫйПчОЛ
  опции профиля:
сообщение 12.9.2008, 10:24
Сообщение #2


Участник
**

Группа: Участник
Сообщений: 154
Регистрация: 10.7.2008
Из: СПб
Пользователь №: 232

Спасибо сказали: 7 раз(а)




Репутация:   5  


Удалено

Сообщение отредактировал ЙаМайскЫйПчОЛ - 12.9.2008, 11:05
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 12.9.2008, 10:32
Сообщение #3


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


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

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

Сообщение отредактировал Tonal - 12.9.2008, 10:34
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AD
  опции профиля:
сообщение 12.9.2008, 10:51
Сообщение #4


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


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

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

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

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

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

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

Сообщение отредактировал AD - 12.9.2008, 11:05
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 12.9.2008, 11:29
Сообщение #5


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Переходи на 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), и если посчиталось - обновится. :)
Ну и данные и потока и главного окна должны быть разные в этом случае.

Сообщение отредактировал Tonal - 12.9.2008, 11:29
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AD
  опции профиля:
сообщение 12.9.2008, 11:40
Сообщение #6


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Цитата(Tonal @ 12.9.2008, 12:29) *
Переходи на 4.4? :)


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

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

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

НЕ, не хочу, чтобы все застывало. В итоге я сделать хочу именно так, чтобы он заполнял по-частям. Но а пока сделать версию, чтобы это заполнение целиком происходило в доп. потоке! То, что работает быстрее, уже проверил! Т.е. приведенный кусок использовать без всяких вэйт кондишен? Мьютекс я использовал уж так, для того, чтобы не забыть, что я планирую в итоге сделать! А щас, хочу, чтобы попросту, после того, как в потоке заполнилось все, оно сразу же отобразилось!

Сообщение отредактировал AD - 12.9.2008, 11:31
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 12.9.2008, 12:03
Сообщение #7


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Я же написал, что нужно изменить - 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 сделать разные для основного потока и для рабочего. Иначе всё равно придётся ждать окончания генерации.

Сообщение отредактировал Tonal - 12.9.2008, 12:03
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AD
  опции профиля:
сообщение 12.9.2008, 12:20
Сообщение #8


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Ну и последний вопросик:
А набросок примерно такой:
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
  опции профиля:
сообщение 12.9.2008, 13:26
Сообщение #9


Профессионал
*****

Группа: Участник
Сообщений: 2003
Регистрация: 4.2.2008
Из: S-Petersburg
Пользователь №: 84

Спасибо сказали: 70 раз(а)




Репутация:   17  


Видимо, где-то напортачил, но вот где? Ломается с сообщением:
Цитата
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();
}


Сообщение отредактировал AD - 12.9.2008, 13:27
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
trdm
  опции профиля:
сообщение 12.9.2008, 13:39
Сообщение #10


Дмитрий Трошин
****

Группа: Участник
Сообщений: 575
Регистрация: 12.1.2008
Пользователь №: 68

Спасибо сказали: 21 раз(а)




Репутация:   6  


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

а сигнал нельзя послать об окончании пересчета?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 3.12.2024, 6:06