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

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

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Потоки при параллельном тестировании оборудования  (Прочитано 17070 раз)
Odyssey
Гость
« : Август 22, 2012, 10:27 »

Здравствуйте!
Имеется набор оборудования, которое надо протестировать. Исправность одного вида оборудования от исправности другого не зависит, поэтому собираюсь запускать тестирование в отдельных потоках (для тестирования каждого вида устройств пишется отдельный класс, наследник QThread). Оборудование может быть слегка глюкавое и завешивать тестирующую программу (например, долго не отвечать по портам и т.п.) поэтому надо предусмотреть "аварийную" остановку и выход из потоков.

Проверку выхода по времени собираюсь осуществлять "извне", т.е. не из самих класов на основе QThread. Однако собственно выход мне пока приходит в голову только по terminate(). Можно ли всё-таки каким-то образом заставить поток остановиться "на середине" и при необходимости "почистить" за собой? Возможно повторное тестирование и применять метод terminate() мне немного неуютно. (Отмечу, что никакие мьютексы и семафоры не используются, также внутри потоков петля событий методом exec() не запускается).

Могу выложить уже написанный код.
« Последнее редактирование: Август 22, 2012, 10:57 от Odyssey » Записан
Bepec
Гость
« Ответ #1 : Август 22, 2012, 10:54 »

Мб тебе стоит подумать об плагинах, или отдельных приложениях?
В результате тебе нужно будет написать программу - менеджера и по программе/плагину для каждого оборудования. И при падении/зависании одного не будет массовых репрессий.

Записан
Odyssey
Гость
« Ответ #2 : Август 22, 2012, 10:55 »

Выложу всё-таки наваянное. (Как-то не уверен в том, что написано хорошо)

Код:
#ifndef ABSTRACTITEMTESTING_H
#define ABSTRACTITEMTESTING_H

#include <QThread>
#include <QObject>

class AbstractItemTesting : public QThread
{
    Q_OBJECT

public:
    AbstractItemTesting(QObject* parent = 0) : QThread(parent)
        { setTerminationEnabled(true); }
    virtual ~AbstractItemTesting() {}
    virtual void run() = 0;
    QString getName() {return itemName;}

protected:
    QString itemName;

signals:
    void testResults(QString itemName_, QString message_, bool noError_);
};

#endif // ABSTRACTITEMTESTING_H

//Класс-пример для тестирования устройства

#ifndef ITEMTESTING1_H
#define ITEMTESTING1_H

#include "abstractitemtesting.h"
#include <QObject>

class ItemTesting1 : public AbstractItemTesting
{

public:
    ItemTesting1(QObject *parent = 0);
    void run();
};

#endif // ITEMTESTING1_H

//Его реализация

#include "itemtesting1.h"
#include <QTimer>
#include <QDebug>

ItemTesting1::ItemTesting1(QObject *parent) : AbstractItemTesting(parent)
{
    itemName = "ItemName1";
}

/*virtual*/ void ItemTesting1::run()
{  
   qDebug() << "Started " << itemName;

   //Имитация тестирования
   msleep(1000);
   emit testResults(itemName, tr("[OK]"), true);
}

//Класс тестирующей системы

#ifndef TESTINGSYSTEM_H
#define TESTINGSYSTEM_H

#include <QObject>
#include <QVector>

//class QVector;
class TestingDialog;
class AbstractItemTesting;

class TestingSystem : public QObject
{
    Q_OBJECT

public:
    TestingSystem(QObject* parent = 0);
    void setMaxTime(long maxTimeMs_);

public slots:
    void startTesting();

private:
    QVector<AbstractItemTesting*> items; //Вектор, содержащий ссылки на тестирующие объекты
    long maxTestTimeMs;                  //Максимальное время тестирования (в миллисекундах)
    int itemsForTest;                    //Общее количество тестируемых устройств
    int reportsResieved;                 //Счетчик полученных отчетов о тестировании
    bool noErrors;                       //Флаг отсутствия сообщений об ошибках в отчетах

private slots:
    void getReport(QString, QString, bool); //Обработка пришедшего сообщения
    void timeExpired();                     //Прекращение тестирования по отсечке времени
};

#endif // TESTINGSYSTEM_H

//Реализация класса тестирующей системы

#include "testingsystem.h"
#include "abstractitemtesting.h"
#include "itemtesting1.h"
#include "itemtesting2.h"
#include "testingdialog.h"
#include <QVector>
#include <QTimer>
#include <QDebug>

const long MAX_TIME_MS = 10000; //рП ХНПМЮБОЙА ПФЧПДЙФУС 10 УЕЛХОД

TestingSystem::TestingSystem(QObject* parent) : QObject(parent), maxTestTimeMs(MAX_TIME_MS)
{
    //Объявление экземпляров классов для тестирования устройств (в каждой системе - свой набор)
    AbstractItemTesting* item1 = new ItemTesting1(this);
    AbstractItemTesting* item2 = new ItemTesting2(this);
    items.append(item1);
    items.append(item2);

    itemsForTest = items.size();        

    for (int itemNum = 0; itemNum < itemsForTest; ++itemNum)
        connect(items[itemNum], SIGNAL(testResults(QString,QString,bool)),
                this, SLOT(getReport(QString,QString,bool)));
}

void TestingSystem::setMaxTime(long maxTimeMs_)
{
    maxTestTimeMs = maxTimeMs_;
}

void TestingSystem::startTesting()
{
    reportsResieved = 0;
    noErrors = true;
    for (int itemNum = 0; itemNum < itemsForTest; ++itemNum)
        items[itemNum]->start();
    QTimer::singleShot(maxTestTimeMs, this, SLOT(timeExpired()));
    qDebug() << "Max Time = " << maxTestTimeMs << " ms";
}

void TestingSystem::getReport(QString itemName, QString message, bool testOk)
{    
    noErrors &= testOk;
    qDebug() << itemName << message << testOk;
    if (++reportsResieved == items.size()) {
        qDebug() << "System test: " << noErrors;        
        //close(0); //??? Не совсем понимаю, как "выходить" из тестирования. Возможно, "грохнув" весь объект
    }
}

void TestingSystem::timeExpired()
{
    for (int itemNum = 0; itemNum < itemsForTest; ++itemNum)
        if (items[itemNum]->isRunning()) {
            items[itemNum]->terminate();
            getReport(items[itemNum]->getName(), "[TIME EXPIRED]", false);
        }
}

//Так будет начинаться тестирование

myTestingSystem = new TestingSystem(this);
myTestingSystem->setMaxTime(5000);
myTestingSystem->startTesting();
Записан
Odyssey
Гость
« Ответ #3 : Август 22, 2012, 11:00 »

Мб тебе стоит подумать об плагинах, или отдельных приложениях?
В результате тебе нужно будет написать программу - менеджера и по программе/плагину для каждого оборудования. И при падении/зависании одного не будет массовых репрессий.



Скажу откровенно - это пока для меня сложновато будет. Как я понимаю, вместо потоков надо использовать процессы (в заглавном сообщении везде поменял все встретившиеся "процессы" на "потоки"). Но и сейчас, вроде бы, не должно быть каких-то особенных репрессий - по истечении срока потоки останавливаются. Вопорс в повторном тестировании и "уборке за собой". Будет ли это проще с процессами?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Август 22, 2012, 11:01 »

Возможно повторное тестирование и применять метод terminate() мне немного неуютно.
"Неуютно" - не то слово Улыбающийся Эта задача частенько мелькает - "зависает" какой-то внешний вызов. Как это решить средствами языка - не знаю. Возможно придется задействовать дуст

http://www.boost.org/doc/libs/1_51_0/libs/exception/doc/tutorial_exception_ptr.html
« Последнее редактирование: Август 22, 2012, 11:04 от Igors » Записан
Odyssey
Гость
« Ответ #5 : Август 22, 2012, 11:05 »

По ссылке 404 выдает.
(Может, сделать в классах-тестировщиках какие-то особые "подчищающие" функции, которые будут вызываться после терминации процесса? Функция getName()-то работает).
Записан
Odyssey
Гость
« Ответ #6 : Август 22, 2012, 11:06 »

А впрочем, ссылка исправилась )) Почитаю...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Август 22, 2012, 11:13 »

То я криво скопировал, исправил. Да, "чиститься" нужно, но для этого нужно получить exception чтобы стек раскрутился. Др варианты явно хуже

- terminate может не сработать вообще
- процессы вместо ниток - ну это тащить "корову на баню" и уродовать всю нормальную логику приложения
Записан
Odyssey
Гость
« Ответ #8 : Август 22, 2012, 11:18 »

Кстати - это мне кажется, или действительно на официальном сайте Qt в главе о стиле программирования не рекомендовано применять try с exception? ^____^
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Август 22, 2012, 11:22 »

Кстати - это мне кажется, или действительно на официальном сайте Qt в главе о стиле программирования не рекомендовано применять try с exception? ^____^
Иногда приходится жертвовать "стилем" чтобы достичь нужного. Да и сами Qt никогда не утверждали что их стилю нужно следовать неукоснительно и спокойно/нормально относятся к исключениям
Записан
Bepec
Гость
« Ответ #10 : Август 22, 2012, 12:07 »

Если спроектировать потоки правильно и поместить все проверки длительности запросов и прочая в них, тогда потоков вполне хватит.
Terminate - фу, кака. У меня оно однажды закадычно продолжало поток до посинения, вешая программу.

PS По идее, поток виснуть не должен при любой ситуации. Т.е. чтоб его положить нереально было входными данными - вот как надо сделать и тогда будет чики-пуки.

PPS внешние вызовы это плохо, особенно если они синхронные.
Записан
xokc
Птица говорун
*****
Offline Offline

Сообщений: 976



Просмотр профиля
« Ответ #11 : Август 22, 2012, 12:29 »

А если там, где-то внутри потока в чужом коде GPF исключение сгенерится - тогда как? Приложению таки смерть придёт?
Имею ввиду вот эту статью (http://habrahabr.ru/post/131412/) и комменты к ней.

Нам в своё время приходилось разрабатывать сервер приложений, который внутри своих потоков (1 поток на одного клиента - клиентов мало, такая модель устраивала) должен был использовать чужие dll. Ничего умнее не придумали, чем от потоков уйти к процессам, т.к. код внутри dll периодически глючил, насмерть убивая сервер, а так - умер процесс, дак туда ему и дорога.
« Последнее редактирование: Август 22, 2012, 12:46 от xokc » Записан
Bepec
Гость
« Ответ #12 : Август 22, 2012, 13:02 »

Вот собственно потому я и советовал в начале темы перейти на процессы Подмигивающий Там не страшно, если отвалится.

Другой вопрос, что если вся программа своя, тогда потоки спокойно будут выполнять свои обязанности Улыбающийся
Записан
Odyssey
Гость
« Ответ #13 : Август 22, 2012, 14:06 »

Ну, программа своя, да оборудование не свое и драйверы к нему - тоже. Так что хотелось бы подстраховаться...
Я, собственно, изначально пытался возложить обязанность останова тестирования на сам класс-наследник QThread. Завел в нем ван-шот от таймера в методе run() с вызовом метода "зачистки и остановки", но у меня ничего не вышло. Петли событий-то нету в общем случае и метод по истечении срока не запускался.
Скажите, а если грохнуть класс-наследник delet'ом, а после вызвать какой-нибудь "уборщик"?

UPDATE: Программа, собственно, не падала и не вываливалась - она завешивалась долгим ожиданием и реакцией железа. Нарушений с памятью и сегментацией, скорее всего, ждать не нужно.
« Последнее редактирование: Август 22, 2012, 14:10 от Odyssey » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Август 22, 2012, 14:14 »

Скажите, а если грохнуть класс-наследник delet'ом, а после вызвать какой-нибудь "уборщик"?
Так если он висит то грохнуть можно только из др нитки - верный краш
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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