Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: Odyssey от Август 22, 2012, 10:27



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

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

Могу выложить уже написанный код.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 10:54
Мб тебе стоит подумать об плагинах, или отдельных приложениях?
В результате тебе нужно будет написать программу - менеджера и по программе/плагину для каждого оборудования. И при падении/зависании одного не будет массовых репрессий.



Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 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();


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 11:00
Мб тебе стоит подумать об плагинах, или отдельных приложениях?
В результате тебе нужно будет написать программу - менеджера и по программе/плагину для каждого оборудования. И при падении/зависании одного не будет массовых репрессий.



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


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 11:01
Возможно повторное тестирование и применять метод terminate() мне немного неуютно.
"Неуютно" - не то слово :) Эта задача частенько мелькает - "зависает" какой-то внешний вызов. Как это решить средствами языка - не знаю. Возможно придется задействовать дуст

http://www.boost.org/doc/libs/1_51_0/libs/exception/doc/tutorial_exception_ptr.html (http://www.boost.org/doc/libs/1_51_0/libs/exception/doc/tutorial_exception_ptr.html)


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


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 11:06
А впрочем, ссылка исправилась )) Почитаю...


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 11:13
То я криво скопировал, исправил. Да, "чиститься" нужно, но для этого нужно получить exception чтобы стек раскрутился. Др варианты явно хуже

- terminate может не сработать вообще
- процессы вместо ниток - ну это тащить "корову на баню" и уродовать всю нормальную логику приложения


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 11:18
Кстати - это мне кажется, или действительно на официальном сайте Qt в главе о стиле программирования не рекомендовано применять try с exception? ^____^


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 11:22
Кстати - это мне кажется, или действительно на официальном сайте Qt в главе о стиле программирования не рекомендовано применять try с exception? ^____^
Иногда приходится жертвовать "стилем" чтобы достичь нужного. Да и сами Qt никогда не утверждали что их стилю нужно следовать неукоснительно и спокойно/нормально относятся к исключениям


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 12:07
Если спроектировать потоки правильно и поместить все проверки длительности запросов и прочая в них, тогда потоков вполне хватит.
Terminate - фу, кака. У меня оно однажды закадычно продолжало поток до посинения, вешая программу.

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

PPS внешние вызовы это плохо, особенно если они синхронные.


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

Нам в своё время приходилось разрабатывать сервер приложений, который внутри своих потоков (1 поток на одного клиента - клиентов мало, такая модель устраивала) должен был использовать чужие dll. Ничего умнее не придумали, чем от потоков уйти к процессам, т.к. код внутри dll периодически глючил, насмерть убивая сервер, а так - умер процесс, дак туда ему и дорога.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 13:02
Вот собственно потому я и советовал в начале темы перейти на процессы ;) Там не страшно, если отвалится.

Другой вопрос, что если вся программа своя, тогда потоки спокойно будут выполнять свои обязанности :)


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

UPDATE: Программа, собственно, не падала и не вываливалась - она завешивалась долгим ожиданием и реакцией железа. Нарушений с памятью и сегментацией, скорее всего, ждать не нужно.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 14:14
Скажите, а если грохнуть класс-наследник delet'ом, а после вызвать какой-нибудь "уборщик"?
Так если он висит то грохнуть можно только из др нитки - верный краш


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 14:18
Что мешает вам самим в потоках проверять время выполнения ваших же функций? Что мешает поставить стража на остановку?

PS или я чего то незнаю, или я чего т не понимаю :)


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: xokc от Август 22, 2012, 14:27
У него висит вовремя выполнения чужих функций из драйверов устройств. Типа readData() - и на ней нитка и умерла. И прервать этот самый readData не получается.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 14:34
xokc прав - примерно это и происходит.
А как реализовать "стража"?


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 14:37
Страж = булевский флаг, но в зависоне он бесполезен.

Если такая ситуация, то отдельные процессы - это простое и вместе с тем хорошее решение. Никаких забот о мусоре и прочем. Отработал и прислал данные - молодец. Работает 2 часа подряд - убил процесс и усё нормально, выдал ошибку юзверю.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 14:45
Скажите, а если грохнуть класс-наследник delet'ом, а после вызвать какой-нибудь "уборщик"?
Так если он висит то грохнуть можно только из др нитки - верный краш

Да, грохаем из другой нитки. Правда, я надеялся, что в деструкторе можно будет освободить какие-нибудь переменные, порт закрыть, не? ^___^

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


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 14:46
Страж = булевский флаг, но в зависоне он бесполезен.

Если такая ситуация, то отдельные процессы - это простое и вместе с тем хорошее решение. Никаких забот о мусоре и прочем. Отработал и прислал данные - молодец. Работает 2 часа подряд - убил процесс и усё нормально, выдал ошибку юзверю.

Спасибо. Надо будет обмозговать и рассмотреть возможность реализации :)


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 14:55
Поток, грохнутый terminate продолжает работать ещё некоторое время. В некоторых случаях (у меня было) он продолжает писать данные, при этом вроде - бы являясь уже убитым :D Чуть не поседел, пока искал среди 2 потоков ошибку, а третий, убитый, весело менял им данные и грохал программу :D


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 15:16
Да, грохаем из другой нитки. Правда, я надеялся, что в деструкторе можно будет освободить какие-нибудь переменные, порт закрыть, не? ^___^
Вызов delete не прервет выполнение нитки нормально "зато" искалечит данные к которым она обратится

Вы, кстати, согласны, что при зависе надежнее работать с процессами, а не с потоками?
Ну это решение просто "потому что нет нормального", всем понятно что оно "через задницу". Я бы прорывался через дуст о котором высокого мнения - хотя и редко его использовал


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: xokc от Август 22, 2012, 15:35
Я бы прорывался через дуст о котором высокого мнения - хотя и редко его использовал
А чем это может помочь прервать зависший вызов?


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 15:46
Я бы прорывался через дуст о котором высокого мнения - хотя и редко его использовал
А чем это может помочь прервать зависший вызов?
Нитка получит исключение и тогда сможет легально выйти


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 15:51
Эммм... А откуда она получит исключение?

Я просто не особо осведомлён о возможностях "дуста", но как он может по результату проверки (долгое время выполнения и прочая лабуда типа устройство зациклилось) выдать ексцепшн до окончания обработки вызова?


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Odyssey от Август 22, 2012, 15:53
Я пока вынужден работать на Qt 4.5.3 под слегка подуродованную версию Линукс (правда, скоро возможен переход на 4.8.2). Boost там работать станет?

И винюсь - пока не очень разобрался в принципе действия. Классу кто-то извне кинет исключение?


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 16:17
Я пока вынужден работать на Qt 4.5.3 под слегка подуродованную версию Линукс (правда, скоро возможен переход на 4.8.2). Boost там работать станет?
Не знаю. Я просто скопировал фолдер дуста, добавил путь в проект и все, с пол-пинка пошло и на Mac и на Вындоуз.

И винюсь - пока не очень разобрался в принципе действия. Классу кто-то извне кинет исключение?
Смысл что если нитка получит исключение - она попадет на Ваш catch, там и зачищайтесь и цивильно выходите. Слышал предлагают это даже добавить в стандарт языка - именно по тем соображениям что Вы изложили (борьба с зависоном). Хз, может в С++ 11 уже добавили (где же эрудиты когда они нужны) ???

Сам не делал (пока не припекло) но присматривался - это есть в Вындоуз (но это 1 платформа) и в дусте.  


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: xokc от Август 22, 2012, 16:37
Классу кто-то извне кинет исключение?
Смысл что если нитка получит исключение - она попадет на Ваш catch, там и зачищайтесь и цивильно выходите.
Давайте еще раз. Внутри метода run в наследнике QThread "завис" метод read из чужой dll. Завис тупо - без таймаутов и exception. Кто в этом случает сгенерит это самое исключение, которое потом будет передано через boost основному потоку?


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 16:41
Igors, ловля исключения даже чужого реализована в С++. Это банальный try/catch.

Но оно работает, если в чужом коде кто-то его бросает. А если чужой код завис к чертям, то не поможет уже ничего :/



Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 17:29
Давайте еще раз. Внутри метода run в наследнике QThread "завис" метод read из чужой dll. Завис тупо - без таймаутов и exception. Кто в этом случает сгенерит это самое исключение, которое потом будет передано через boost основному потоку?
Я сам (т.е. прерывающий зависание) напр в главной нитке. "Просто так" это ничего не даст потому что на др нити это никак не повлияет. Речь идет о механизме передачи exception в др нити.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 22, 2012, 19:12
Эмм.. Igors а зачем?

ТС же ясно сказал - тесты независимые. Каждый тест отдельная нить.
Если нить зависает, её надо прибить без последствий и выдать ошибку. Exception'ов внутри внешних вызовов нет. Виснет вся нить, что делать? :)


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 22, 2012, 19:20
Эмм.. Igors а зачем?

ТС же ясно сказал - тесты независимые. Каждый тест отдельная нить.
Если нить зависает, её надо прибить без последствий и выдать ошибку. Exception'ов внутри внешних вызовов нет. Виснет вся нить, что делать? :)
Знову за рибу грошi  :'(  Нет способа "прибить без последствий", но можно снять ее с ожидания передав ей exception "извне". Это совсем не ново, вот напр один клепал, правда под Вындоуз

http://www.codeproject.com/Articles/71529/Exception-Injection-Throwing-an-Exception-in-Other (http://www.codeproject.com/Articles/71529/Exception-Injection-Throwing-an-Exception-in-Other)


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: xokc от Август 23, 2012, 08:20
То есть, нужно придумать неких диспетчер потоков, который анализирует работоспособность (независшесть) каждой из нитей и в случае проблем с ней генерит для неё exception, "проталкивает" его внутрь нити, нить самостоятельно завершается, всё за собой подчищает и сообщает об этом диспетчеру. При этом основное приложение живёт нормальной жизнью.
Как-то уж больно заманчиво всё это выглядит, чтобы уже не быть реализованным в виде готового решения.

Что касается C++ 0x, то вот цитата от Страуструпа (там, вообще много интересного http://sergeyteplyakov.blogspot.com/2012/05/c-11-faq.html):
Не существует возможности запросить поток завершить исполнение (например, потребовать его завершиться как можно быстрее и насколько возможно безопасно), или принудительно прервать его исполнение (т.е. убить его). Мы оставили следующие возможности для реализации такого поведения:
- Разработать собственный механизм кооперативный отмены исполнения (с помощью разделяемых данных, вызывающий поток может установить значение, которое будет прочитано вызываемым потоком для быстрого и безопасного завершения),
- С помощью «нативного подхода» (получив доступ к дескриптору операционной системы с помощью thread::native_handle()),
- Завершить процесс (с помощью std::quick_exit()),
- Завершить программу (с помощью std::terminate()).


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Bepec от Август 23, 2012, 09:27
Igors, а можно маленький примерчик с комментариями? В виде проекта допустим?

Очень уж заманчиво выглядит как бэ.


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: Igors от Август 23, 2012, 14:38
То есть, нужно придумать неких диспетчер потоков, который анализирует работоспособность (независшесть) каждой из нитей и в случае проблем с ней генерит для неё exception, "проталкивает" его внутрь нити, нить самостоятельно завершается, всё за собой подчищает и сообщает об этом диспетчеру. При этом основное
приложение живёт нормальной жизнью.
Именно. Все корректно/легально

Как-то уж больно заманчиво всё это выглядит, чтобы уже не быть реализованным в виде готового решения.
Дуст - вполне авторитетный источник. Эту ссылку я уже приводил, но наверное затерялась  :)
http://www.boost.org/doc/libs/1_39_0/libs/exception/doc/tutorial_exception_ptr.html (http://www.boost.org/doc/libs/1_39_0/libs/exception/doc/tutorial_exception_ptr.html)
Судя по первой фразе - реализовано. Дальше правда надо вникать тшательно, ну дело того стоит

Igors, а можно маленький примерчик с комментариями? В виде проекта допустим?

Очень уж заманчиво выглядит как бэ.
Не понял, а у кого правая рука Вындоуз, у Вас или меня? Ну вот Вам и карты в руки
http://msdn.microsoft.com/en-us/library/dd293602.aspx (http://msdn.microsoft.com/en-us/library/dd293602.aspx)


Название: Re: Потоки при параллельном тестировании оборудования
Отправлено: xokc от Август 24, 2012, 09:20
Дуст - вполне авторитетный источник. Эту ссылку я уже приводил, но наверное затерялась  :)
Судя по первой фразе - реализовано. Дальше правда надо вникать тшательно, ну дело того стоит
Я немного другое имел ввиду. Хотелось бы видеть реализацию не пробрасывания exception из потока в поток, а весь функционал в виде готового "зависоустйчивого" диспетчера. Наверняка в рамках какого-нибудь из opensource проектов уже всё сделано.