crossplatform.ru

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


  Ответ в Проблема с правильным завершением дополнительного потока
Введите ваше имя
Подтвердите код

Введите в поле код из 6 символов, отображенных в виде изображения. Если вы не можете прочитать код с изображения, нажмите на изображение для генерации нового кода.
 

Опции сообщения
 Включить смайлы?
Иконки сообщения
(Опционально)
                                
                                
  [ Без иконки ]
 


Последние 10 сообщений [ в обратном порядке ]
AD Дата 19.9.2008, 11:01
  Не знаю уж чего я там еще такого наваял, но пока вроде бы работает без багов! :)
Tonal Дата 19.9.2008, 10:11
  Извини, правда некогда разбираться. :(
Но, если ты таки хочешь работать с потоками, стоит самому разобраться - я же твою прогу писать не буду. :)
AD Дата 15.9.2008, 11:14
  Поправил немного. Но все-равно, работает кривовато. Причем глюки очень разнообразные: то график отображается параметра, который выбран не в данный момент, а во время предыдущей выборке, то совсем не отображается, то застревает на этапе выборки. Может быть сможете подсказать, где копнуть, чтобы поправить это безобразие?
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();
}

AD Дата 12.9.2008, 16:52
  Немного изменил. Все-равно ломается, во-первых, правда, в другом месте.
Подскажите, пожалуйста, где ошибка.
Правка произошла в функции 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()!
Я уже запутался, где ошибка.
trdm Дата 12.9.2008, 13:39
 
Цитата(Tonal @ 12.9.2008, 13:03) *
Причём эту test4Update() дёргать или в идле, или по таймеру, пока не посчитается.

а сигнал нельзя послать об окончании пересчета?
AD Дата 12.9.2008, 13:26
  Видимо, где-то напортачил, но вот где? Ломается с сообщением:
Цитата
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, 12:20
  Ну и последний вопросик:
А набросок примерно такой:
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 вообще локально.
Tonal Дата 12.9.2008, 12:03
  Я же написал, что нужно изменить - 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 Дата 12.9.2008, 11:40
 
Цитата(Tonal @ 12.9.2008, 12:29) *
Переходи на 4.4? :)


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

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

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

НЕ, не хочу, чтобы все застывало. В итоге я сделать хочу именно так, чтобы он заполнял по-частям. Но а пока сделать версию, чтобы это заполнение целиком происходило в доп. потоке! То, что работает быстрее, уже проверил! Т.е. приведенный кусок использовать без всяких вэйт кондишен? Мьютекс я использовал уж так, для того, чтобы не забыть, что я планирую в итоге сделать! А щас, хочу, чтобы попросту, после того, как в потоке заполнилось все, оно сразу же отобразилось!
Tonal Дата 12.9.2008, 11:29
  Переходи на 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), и если посчиталось - обновится. :)
Ну и данные и потока и главного окна должны быть разные в этом случае.
Просмотр темы полностью (откроется в новом окне)
RSS Текстовая версия Сейчас: 29.3.2024, 2:16