Russian Qt Forum

Qt => Вопросы новичков => Тема начата: CPP11 от Декабрь 07, 2014, 23:24



Название: потоки
Отправлено: CPP11 от Декабрь 07, 2014, 23:24
Доброго времени суток. В ближайшее время нужно будет написать многопоточное приложение на QT, поэтому прошу прояснить некоторые моменты...
Суть приложения - есть 2 потока - Gui и General.
Gui - отвечает за пользовательский интерфейс и принимает от системы сообщения о подключаемом оборудовании(WM_DEVICECHANGE, только windows).
General - обрабатывает команды от Gui и сигнализирует в ответ об изменениях, которые нужно отобразить, обрабатывать может очень долго, потому и выделен в отдельный поток.
Насколько я понял второй поток нужно создавать как в этой заметке (http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/):
Код:
//worker.h
class Worker : public QObject{
//...
}
//main.cpp
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
//connect'ы
thread->start();
и пока я не уничтожу объект worker поток не завершится и будет обрабатывать сигналы. И в этой конструкции, если поток Gui отправит последовательно два сигнала, то я могу быть уверен, что другой поток обработает эти сигналы именно в этой последовательности и дополнительной синхронизации не нужно. Это верно?


Название: Re: потоки
Отправлено: Johnik от Декабрь 08, 2014, 00:44
и пока я не уничтожу объект worker поток не завершится и будет обрабатывать сигналы.
нет, поток не завершится, пока вы его не остановите: quit() или terminate().
И в этой конструкции, если поток Gui отправит последовательно два сигнала, то я могу быть уверен, что другой поток обработает эти сигналы именно в этой последовательности и дополнительной синхронизации не нужно. Это верно?
да, верно.


Название: Re: потоки
Отправлено: Igors от Декабрь 08, 2014, 12:17
если поток Gui отправит последовательно два сигнала, то я могу быть уверен, что другой поток обработает эти сигналы именно в этой последовательности и дополнительной синхронизации не нужно. Это верно?
Для только одного "отправляющего" - верно, для 2 и более - нет


Название: Re: потоки
Отправлено: Old от Декабрь 08, 2014, 12:19
если поток Gui отправит последовательно два сигнала, то я могу быть уверен, что другой поток обработает эти сигналы именно в этой последовательности и дополнительной синхронизации не нужно. Это верно?
Для только одного "отправляющего" - верно, для 2 и более - нет
Два Gui потока в Qt? Это фантастика.


Название: Re: потоки
Отправлено: Igors от Декабрь 08, 2014, 12:54
Два Gui потока в Qt? Это фантастика.
"Напрасно, все меняется очень быстро"  :) C 2 и более - один их самых "кровавых" багов с которыми я имел дело


Название: Re: потоки
Отправлено: Old от Декабрь 08, 2014, 13:15
C 2 и более - один их самых "кровавых" багов с которыми я имел дело
Что вы называете "кровавым" багом?
То что если два потока добавляют сообщения в одну очередь сообщения могут чередоваться?


Название: Re: потоки
Отправлено: Igors от Декабрь 08, 2014, 13:33
То что если два потока добавляют сообщения в одну очередь сообщения могут чередоваться?
Да. Псевдокод (если не ошибаюсь, в 3-й раз)
Цитировать
SendEvent(receiver, jobAbort, ...);
SendEvent(receiver, resetStatus, ...);
Это не вызывает сомнений, действительно resetStatus выполнится после jobAbort. Но это совсем не значит что resetStatus будет обрабатываться сразу после jobAbort (хотя мы их и посылали немедленно одно за другим). При 2 и более отправителях др события могут вклиниться в очередь между нашими 2-мя.. изменить контекст получателя и тогда..

Конечно, это "совсем очевидно"  :)



Название: Re: потоки
Отправлено: Old от Декабрь 08, 2014, 13:40
Конечно, это "совсем очевидно"  :)
Ну, как бы, да. :)
Очереди сообщений это все те же очереди данных.


Название: Re: потоки
Отправлено: Johnik от Декабрь 08, 2014, 13:42
Для только одного "отправляющего" - верно, для 2 и более - нет
пример приведите, где не верно
по моему опыту и более 10 потоков работало


Название: Re: потоки
Отправлено: Igors от Декабрь 08, 2014, 13:48
пример приведите, где не верно
по моему опыту и более 10 потоков работало
И по моему тоже. Просто раз в месяц пользователь сообщал о каком-то вылете. Ну добавил логи, потом еще их расширил - возился долго. Обратите внимание на "именно в этой последовательности". Так вот, при 2 и более отправителях последовательность НЕ "именно та".


Название: Re: потоки
Отправлено: CPP11 от Декабрь 08, 2014, 19:05
нет, поток не завершится, пока вы его не остановите: quit() или terminate().
Значит для корректного завершения потока нужно выполнить примерно такой код:
Код:
thread->quit();
if(thread->wait(1000))
{
delete worker;
delete thread;
}
Или worker нужно сперва переместить обратно? Или он сам убьётся вместе с потоком?


Название: Re: потоки
Отправлено: Igors от Декабрь 08, 2014, 19:14
Без всяких 1000 - просто thread->wait()


Название: Re: потоки
Отправлено: CPP11 от Декабрь 08, 2014, 19:29
Без всяких 1000 - просто thread->wait()
нет, ну 50 дней его ждать точно не нужно, если за секунду не умер, то пусть аварийно завершается :) .


Название: Re: потоки
Отправлено: Johnik от Декабрь 08, 2014, 19:30
Так вот, при 2 и более отправителях последовательность НЕ "именно та".
"кто успел тот и съел"


Название: Re: потоки
Отправлено: Igors от Декабрь 08, 2014, 19:45
нет, ну 50 дней его ждать точно не нужно, если за секунду не умер, то пусть аварийно завершается :) .
Давайте без самодеятельности, аварийное завершение - себе дороже. Единственный способ завершить thread - дать ей нормально выйти. Никуда не денется - выйдет, не надо дергаться


Название: Re: потоки
Отправлено: CPP11 от Декабрь 08, 2014, 20:05
Давайте без самодеятельности, аварийное завершение - себе дороже. Единственный способ завершить thread - дать ей нормально выйти. Никуда не денется - выйдет, не надо дергаться
Это ошибочный подход, лучше завершить/перезапустить зависшее приложение, чем оставлять его на самотёк. Это негативно сказывается на юзабилити и усложняет сбор сведений о сбоях для дальнейшего анализа.


Название: Re: потоки
Отправлено: Alexu007 от Декабрь 09, 2014, 00:29
Так вот, при 2 и более отправителях последовательность НЕ "именно та".
"кто успел тот и съел"
От решаемой задачи зависит. Почему бы в сообщения не вставить порядковые номера, а получатель пусть решает, как поступать с сообщениями - сразу обрабатывать или сбросить в буфер и пусть ждёт очереди? Но если будет много инфы в очереди - то может здесь вообще потоки лучше не использовать? Сложный алгоритм работы с очередью сообщений съест эффект быстродействия потоков.


Название: Re: потоки
Отправлено: Johnik от Декабрь 09, 2014, 00:57
От решаемой задачи зависит. Почему бы в сообщения не вставить порядковые номера, а получатель пусть решает, как поступать с сообщениями - сразу обрабатывать или сбросить в буфер и пусть ждёт очереди? Но если будет много инфы в очереди - то может здесь вообще потоки лучше не использовать? Сложный алгоритм работы с очередью сообщений съест эффект быстродействия потоков.
О каких сообщениях и очередях идет речь? CPP11 в начале говорил об механизме сигналов/слотов. Сигналы попадают в очередь сообщений Qt, и именно Qt распределяет их по потокам.


Название: Re: потоки
Отправлено: Igors от Декабрь 09, 2014, 14:45
Это ошибочный подход, лучше завершить/перезапустить зависшее приложение, чем оставлять его на самотёк. Это негативно сказывается на юзабилити и усложняет сбор сведений о сбоях для дальнейшего анализа.
А Вы можете подтвердить Ваши слова кодом? (хотя бы псевдокодом)
Код
C++ (Qt)
thread->quit();
if(thread->wait(1000))
{
delete worker;
delete thread;
}
else {
...  // и что здесь??
}
 


Название: Re: потоки
Отправлено: CPP11 от Декабрь 09, 2014, 18:27
Igors, это зависит от контекста. Часто нужно сохранить промежуточное состояние приложения и после перезапуска попытаться вернуть его в это состояние. Естественно нужно зафиксировать сам факт падения и информацию, которая может помочь выявить причину - обычно это дамп процесса(чаще минидамп + состояние объектов), если программа полагается на сторонние компоненты, то имеет смысл сохранить и информацию о них. После перезапуска приносим извинения пользователю и отправляем себе отчёт об ошибке (некоторые приложения просят пользователя подтвердить отправку, т.к. в таких дампах может содержаться приватная информация).


Название: Re: потоки
Отправлено: Igors от Декабрь 09, 2014, 18:49
Igors, это зависит от контекста. Часто нужно сохранить промежуточное состояние ...
Ну и что Вы будете сохранять в данном случае? Откуда Вы возьмете промежуточное состояние - или вообще хоть какую-то информацию достойную сохранения/посылки? И почему такая ситуация вообще расценивается как "падение"? Нитка не завершилась через 1 секунду? Так что с того, такое вполне вполне возможно если напр было распределено гораздо больше памяти чем физически имеет хилая машина, или хороший файл мапнут в память (вындоуз пошла по свопам).

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


Название: Re: потоки
Отправлено: CPP11 от Декабрь 09, 2014, 19:41
Ну и что Вы будете сохранять в данном случае?
Да элементарно - тут нужно завершить приложение, так что просто убиваем свой процесс, а ОС всё подчистит. В противном случае получим зависшее окно, которое будет мозолить глаза пользователю, который его рано или поздно убьёт, вспоминая вас добрым словом. А если окна не будет, то процесс просто будет висеть и жрать ресурсы, пока система не перезагрузится, что ещё хуже.
Нитка не завершилась через 1 секунду?
не надо цепляться к этой цифре, можно выбрать свою в зависимости от ситуации, в моём случае подвисать там нечему, поэтому выбран период, который не позволит пользователю заетить подвисание. Возможно, 5 секунд будет лучшим выбором для Windows, чтобы дождаться повышения приоритета на случай загруженности процессора. Как бы то ни было, это оффтоп, вопрос был не о выборе времени ожидания завершения процесса.