Как стать автором
Обновить

Комментарии 12

wait(); // неограниченное ожидание завершения потока

Результатом может быть "замораживание" графического интерфейса (если он будет использоваться), очереди событий и т.д. Думаю, лучше использовать подобный код:

void waitForEndThread(QThread *obj, unsigned long time)
{
    while (!obj->wait(time)) {
        QCoreApplication::processEvents();
    }
}

Во-первых, спасибо. И это, действительно, актуально. Я до сих пор не знаю, как жестко удалить поток. А, похоже, такая возможность должна существовать. И Вы правы - замораживается. Но, чем отличатся этот код.

ThCounter::~ThCounter(void) {
    bIfRun = false;
    QThread *obj = QThread::currentThread();
    obj->quit();
    bool w = obj->wait(10000);
    if (nIdTimer) killTimer(nIdTimer);
    nIdTimer = 0;
}

От следующего

ThCounter::~ThCounter(void) {
    bIfRun = false;
    quit();         // корректный способ завершения потока
    wait(10000);         // ожидание завершени потока (неограниченное ожидание wait())
                    // подробнее см. Боровский А. Qt4.7... стр.170
    if (nIdTimer) killTimer(nIdTimer);
    nIdTimer = 0;
}

А они отличаются. Функциональностью. ОБъясню в чем проблема. Нижний "зависает". на wait(n). Верхний - нет. Для меня - загадка. Казалось бы, если я задал параметр методу wait, то на нем и "вис" будет это же время. Так работает нижний код. ВЕрхний почему-то работает иначе, т.е., несмотря на указанный параметр, выход из wait происходит тут же. Это и позволяет не блокировать интерфейс. Можно грешить на то, что возвращается указатель не на текущий поток. Пусть так. Но все равно - почему фукции wait отрабатывают по-разному?

Повторюсь. Мне хотелось бы знать, как быстро "убить" поток. Но как?

Подозреваю, что оба фрагмента кода будут отличаться набором ошибок и количеством утекаемой памяти.

Если вы хотите запустить дочерний поток из основного потока (или еще откуда-то) и дождаться окончания выполнения (естественного или по соотв.сигналу), то вам нужно действовать примерно так:

ThreadWorker worker; // либо через оператор new
worket.init(setup_params); //необязательное. setup_params набор параметров, которые надо отдать в поток
worket.start(); //начинается выполнение содердимого переопределенного метода run()
worker.waitForEndThread(100); //мой метод, описанный выше
//... other code

В строке 4 внутри метода каждые 100 мс wait() будет сбрасываться в true и будет обрабатываться очередь сообщений (сигналы слотам). Потом в цикле все будет повторяться, пока не будет выполнен выход из метода run().

Основной поток будет ждать, пока не закончится выполнение дочернего потока и не будет так, что основной поток закончил свое выполнение и вызываются деструкторы для освобождения ресурсов, а дочерний поток все еще выполняется.

Вообще, потоки - специфическая вещь и для каждой конкретной задачи нужна своя реализация.

Пока я писал ответ, просмотрел весь ваш код - в вашем случае, возможно, стоило создавать потоки через moveToThread(). Остальное советую глянуть в справке по Qt - там есть примеры.

В деструкторе ThreadWorker::~ThreadWorker() удалять динамически созданные члены класса, закрыть используемые ресурсы и т.д.

Поток - это независимая задача, которая выполняется внутри процесса и разделяет с ним общее адресное пространство, код и глобальные данные.

Казалось бы простая и прозрачная концепция. И я точно также могу определить, что ...

Автомат - это независимая задача, которая выполняется внутри процесса и ... далее по тексту.

Вся разница в том, что поток - это поток, а автомат - это внутри одного потока. Именно так это сейчас, если говорить об автоматах. Другая разниза - .в модели управления. У потока - это блок-схемная модель, у автомата - это соотвественно автомат.

И, вроде бы, совсем все просто взять и сделать ядро ВКПа автоматным, просто перейдя с одного потока на их множество и будет сплошное счастье. Другими словами - сменить один двигатель на другой. Однако в текущем варианте я не испытываю проблем с программированием, а с потоками, повозившись с ними, я понимаю, что возникнет множество почти неразрешимых проблем. Можно сказать, что это мои личные проблемы, но...

Предположим, я решусь на подобное. Сейчас автомат - это потомок моего автоматного класса LFsaAppl. Сделать его еще и потомком класса QThread - пару секунд. Т.е. уже это сделано (см. мою статью). Но замена "двигателя" сразу породила проблемы: что-то перестало работать. Я уж молчу про саму отладку, с которой у меня с автоматами совсем нет проблем.

И вот я возился с QThread больше месяца, постигая все чудачества потоков. И тут появляется, как я понял, его аналог - ThreadWorker. И где гарантии, что я не приду к такому же итогу?...

А потому. за информацию огромнейшее спасибо. Отойду от QThread и возьмусь, может быть, за ThreadWorker. Но подозреваю, что поменяв "двигатель" я буду сильно жалеть о старом ;) Ведь, на нем, кроме скорости, меня все устраивало. А тут со скоросьтю будет моментами все просто прекрасно, но без каких либо гарантий доехать до места.

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

Пока же, чтобы разгрузить автоматное ядро, я проверенные автоматы перевел на таймеры (а планы были на потоки). Так у меня теперь работает весь севис среды ВКПа. Это проверенный и всегда (пока проблем не было) работающий примем. Но стоит только перейти какому-то процессу на поток - бац! - проблемы. Конкретный пример. В КПа есть процесс, который я называю осциллограф (все графики на нем) . Был автоматным - работал. Стал таймерным - работает. Переношу на поток - не работает от слова совсем. И попробй докопаться. Хотя именно только включение одного осциллографа тормозит ядро больше чем на порядок. На том же тесте счетчиков имеем: дискретный такт без осциллографа - 0.28 мсек, а с ним - 2.8 мсек. Но все мои заходы на "поточную графику" пока ни к чему не приводят. Хотя и совсем бесполезными их не считаю :)

Дабы не плодить на техническом ресурсе бесполезную лирику, предлагаю проанализировать то, что написано в книге Боровский и у вас. Тем более у меня нет русской раскладки, чтобы быстро отвечать вам.
У Боровский в методе run() выполняется:

  • создание локальной переменной, ее инициализация (параметры, связывание через connect())

  • запуск

  • вызов exec()

In the Qt Documentation:
int QThread::exec()
Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit().
This function is meant to be called from within run(). It is necessary to call this function to start event handling.
Note: This can only be called within the thread itself, i.e. when it is the current thread.

О, есть цикл обработки событий внутри дочернего потока.
Идем далее.
У меня нет полных исходников примера из Боровский, но переменная thread объявлена в классе главного окна. Ее срок жизни - срок жизни приложения, т.е. главного потока.
Смотрим, что будет при завершении приложения.
Смотрим деструктор:

  • проверка того, что thread != nullptr

  • вызов quit()

  • вызов wait() //с дефолтными параметрами

In the Qt Documentation:
void QThread::quit()
Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).
This function does nothing if the thread does not have an event loop.
Note: This function is thread-safe.

bool QThread::wait(QDeadlineTimer deadline = QDeadlineTimer(QDeadlineTimer::Forever))
Blocks the thread until either of these conditions is met:
The thread associated with this QThread object has finished execution (i.e. when it returns from run()). This function will return true if the thread has finished. It also returns true if the thread has not been started yet.
The deadline is reached. This function will return false if the deadline is reached.
A deadline timer set to QDeadlineTimer::Forever (the default) will never time out: in this case, the function only returns when the thread returns from run() or if the thread has not yet started.
This provides similar functionality to the POSIX pthread_join() function.

Ага, есть метод отправки сигнала (QThread::quit()) на завершение выполнения дочернего потока, которую примет цикл обработки событий (QThread::exec()).
После получения сигнала на завершение, будет выход из метода run(), который по сути означает завершение выполнения потока и освобождение ресурсов.

Мы ждем корректного завершения потока (QThread::wait()) и освобождения связанных с ним ресурсов во избежание их утечки. Так как в примере в потоке ничего особенного не выполняется, то задержка на wait() будет мизерной.

Код примера из Боровский полностью корректный для использования в той ситуации.

Теперь смотрим на ваш код (Листинг 1).
Внутри метода run() есть цикл обработки данных и нет цикла обработки событий.
Внутри FThCounter::~FThCounter() и FThCounter::WaitForThreadToFinish():

  • вызов quit()

  • вызов wait() //с дефолтными параметрами

Касательно разницы в работе. Надо смотреть под отладчиком, что именно возвращает QThread::currentThread() и есть ли потоки с таким ID (см справку).

Метод quit() отправляет команду на завершение выполнения дочернего потока. Вот только чем и где он будет принят - я сказать не могу. У меня только одна ассоциация: "Uciekł gdzie pieprz rośnie" (в неизвестном направлении). Нужно смотреть исходники QThread, но ваш код - потенциальный UB.
Метод wait() - может ждать бесконечно долго или сразу возвратить true, в зависимости от состояния экземпляра класса FThCounter.

И главное, в вашем листинге я нигде не нашел цикла обработки событий, т.е. послать дочернему потоку сигнал на выход из цикла проблематично - нет гарантии, что он вовремя будет обработан. Также будет замораживаться очередь сообщений приложения (см. мои комментарии).

Мне хотелось бы знать, как быстро "убить" поток. Но как?

Добавьте возможность обработки очереди сообщений в основном или дочернем потоке. Тогда и фризы исчезнут и потоки будут вовремя убиваться.

Как бы то, что есть, меня фактически устраивает, но... есть определенные сомнения. Особенно по поводу сообщений. Они возникли пости сразу, т.к. когда, я во внутрь run() вставляю exec(), то на нем все и виснет. Это сразу видно в отладчике. При удалении объекта-потока процесс идет дальше. Использую такую хитрость можно, конечно, все же запустить. Но поток-то запустится, но тогда прекратится обработка сообщений. Или нет? Что здесь не так?

Вы хоть читали документацию?

Enters the event loop and waits until exit() is called, returning the value that was passed to exit().

exec() это цикл обработки событий! На нем все будет виснуть, т.к. он обрабатывать только очередь событий. Ваш случай - мой вариант waitForEndThread() или изменение архитектуры приложения так, чтобы в quit() или wait() не было необходимости.

PS. Если это какой-то коммерческий/полукоммерческий проект - наймите фрилансера с необходимыми скиллами.

Что-то не нашел класса ThreadWorker? Где он находится или из какой библиотки взят?

Это просто абстрактный пример класса, который реализует поток. Хотя, примеры взяты из кода моих проектов.

"Для порождения потока в Qt нужно создать потомок класса QThread и перегрузить его метод run(). "
Разве от этого многообразие средств Qt будет утеряно. Вот нужен мне в этом отдельном потоке QTimer... И как его использовать? А механизм signal-slot будет работать в экземпляре класса с переопределенным run? Может быть лучше использовать thread and worker подход? Я написал такой пример. Он использует thread and worker подход, но в отличие от всего того, что встречал в интернете, у меня смарт points. Хотел бы получить конструктивную критику по своему примеру...

...Разве от этого многообразие средств Qt будет утеряно.

С Qt все останется так, как и было. Станет только лучше - появятся автоматы в дополнение к потокам и всему тому, что есть в Qt.

Вот нужен мне в этом отдельном потоке QTimer... И как его использовать?

С этим похоже, действительно, проблемы. А вот один таймер в поток устроен по умолчанию. С ним и его использованием проблем нет. Так что не все так уж плохо.

Хотел бы получить конструктивную критику по своему примеру...

Если вопрос ко мне, то пока я не дорос до такого. В потоках я еще только начинающий ;)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории