2013-11-19 3 views
2

я наткнулся на эту проблему, как и другие имущих: QThread won't stop/does not process a signal QThread - Using a slot quit() to exit the threadКакова функция QThread.wait()?

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

Но, похоже, это невозможно в Qt. Причина в том, что слот рабочего QThread.quit() рабочего не может быть передан из самого потока. Цикл событий, который прослушивает сигналы в этом слоте, должен находиться в том же потоке, который создал рабочий поток. Это означает, что поток создания не должен блокироваться, иначе рабочий поток никогда не останавливается.

Что подводит меня к моему вопросу, что в чем смысл QThread.wait()? Я думаю, что эта функция должна просто застрять в конце программы, чтобы убедиться, что все потоки вышли, но фактически не может использоваться для синхронизации потоков, по крайней мере, она не может использоваться для синхронизации рабочего потока, с созданным потоком Это. Поскольку, если QThread.wait() вызывается из создающего потока, он блокирует свой цикл событий, который блокирует интерфейс рабочего потока, что предотвратит его выход из него.

Я что-то упустил?

Я думал, что мне нужно, чтобы добавить фрагмент кода:

for (auto i = myVector.begin(); i < myVector.end(); ++i) 
{ 

    // 5-line best practice creation for the thread 
    QThread* workerThread  = new QThread; 
    MyWorkerObject* workerObject = new MyWorkerObject(0); 
    workerObject->moveToThread(workerThread); 
    QObject::connect(workerThread, SIGNAL(started()), workerObject, SLOT(init())); 
    QObject::connect(workerThread, SIGNAL(finished()), workerObject, SLOT(deleteLater())); 

    // Stop mechanism 
    QObject::connect(workerObject, SIGNAL(finished()), workerThread, SLOT(quit())); 

    // Start mechanism 
    wokerThread->start(); 

    // Invoking the work 
    QMetaObject::invokeMethod(workerObject, "StartYourJob", Qt::QueuedConnection, Q_ARG(SomeType, *i)); 

    // Synchronization 
    workerThread->wait(); 
    delete wokerThread; 
} 
+3

'Я хочу, чтобы выполнение в коде, которое создает рабочий поток, должно быть остановлено, пока рабочий поток не выполнит свою работу.' Похоже, что вам не нужен ниток. –

+0

Alex, как я уже упоминал, работа работника «включает отправку сигналов в другие потоки в моем коде и получение сигналов асинхронно». Если я вообще не создаю нить, не будет цикла событий для прослушивания сигналов ... –

+0

'QThread :: wait' необходим для очистки. Он ожидает завершения тайм-аута или потока. Вы не должны удалять объект запуска потока, поскольку он может привести к непредвиденным ошибкам, поэтому перед удалением вы должны называть 'wait (someInterval)' –

ответ

1

Цель нитей, чтобы позволить процессам выполняться одновременно, так что если вы просто создание потока к (в то же время!) работать и ждать в текущем потоке, вам не нужно использовать новый поток.

Чтобы ответить на вопрос о цели QThread :: wait(), в документации Qt указано, что она похожа на функцию POSIX pthread_join. Быстрый поиск по pthread_join показывает this link, в котором говорится, суть заключается в следующем: -

Функция pthread_join() является удобство, которое доказало свою полезность в многопоточных приложений. Верно, что программист мог имитировать эту функцию, если она не была предоставлена ​​путем передачи дополнительного состояния как часть аргумента start_routine(). Оконечная нить установит флаг для обозначения завершения и трансляции состояния , которое является частью этого состояния; соединительная нить будет ждать по этой переменной . Хотя такая методика позволит потоку ожидать более сложные условия (например, ожидание завершения нескольких потоков ), ожидание завершения отдельного потока считается весьма полезным. Кроме того, в том числе функция pthread_join() никоим образом не исключает программиста от кодирования таких сложных ожиданий. Таким образом, в то время как не примитивный, в том числе pthread_join() в этом объеме POSIX.1-2008 считался ценным.

Функция pthread_join() предоставляет простой механизм, позволяющий приложению ожидать завершения потока. После завершения потока приложение может затем выбрать очистку ресурсов, которые были использованы в потоке.Например, после возврата pthread_join() может быть исправлено любое хранилище стека, предоставленное приложением. Функция

pthread_join() или pthread_detach() должен в конечном счете будет вызывается для каждого потока, который создается с атрибутом detachstate набором для PTHREAD_CREATE_JOINABLE, так что хранилище, связанное с резьбы может быть утилизировано.

Взаимодействие между pthread_join() и отмены является хорошо определены по следующим причинам:

Функция

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

Аннулирование не может произойти в состоянии отмены отмены.

Таким образом, необходимо учитывать только состояние отмены по умолчанию. Как указано , либо вызов pthread_join() отменен, либо он преуспевает, , но не оба. Разница очевидна для приложения, так как выполняется либо обработчик отмены, либо возвращается pthread_join(). Там не являются условиями гонки, так как pthread_join() был вызван в состоянии отсрочки .

Если реализация определяет, что значение, указанное в аргументе нить к pthread_join() не относится к подключаемом нити, то рекомендуется функция должна обязательно и сообщать о [EINVAL] ошибку.

Если реализация определяет, что значение, указанное в аргументе нить к pthread_join() относится к вызывающему потоку, то рекомендуется функция должна обязательно и сообщать о [EDEADLK] ошибку.

Если реализация обнаруживает использование идентификатора потока после окончания его срока службы , рекомендуется, чтобы функция была неудачной и сообщила ошибку [ESRCH].

+0

Мерлин, как бы вы это сделали? Рабочий поток содержит рабочий объект (workerobject-> movetothread (workerthread)), задачей которого является отправка нескольких сигналов в разные потоки в коде и ожидание их ответов через его слоты. Если рабочий объект не содержится в рабочем потоке, как еще он может получать сигналы от других потоков? Для этого нужен цикл событий. Возможно, мне действительно нужен QEventLoop, но я подумал, что было бы целесообразно сделать это через рабочий поток. –

+0

@AliB. Трудно посоветовать, не видя своего текущего кода, но похоже, что рабочий поток должен быть вашим основным потоком, предполагая, что вы создали его как объект в другом потоке. При вызове QApplication :: exec() основной поток автоматически имеет цикл событий. Если рабочий объект не делает очень многого, но координирует другие потоки, тогда он должен быть хорошо на основном потоке. – TheDarkKnight

+0

Спасибо, Мерлин, добавил код. У меня есть цикл, и я хочу создать поток для выполнения задания для каждого элемента. Я могу сделать это другим, но неудобным образом. Но я хочу понять, как это сделать удобно, без необходимости разбивать мой цикл на переменные-члены создаваемого потока и иметь сигналы для его увеличения и т. Д. –

0

QThread :: wait() не то, что вам нужно. Эта функция является именно тем, что вы упомянули, она ждет завершения потока.

BOOL QThread :: ждать (неподписанных долгое время = ULONG_MAX)

Блокирует нить, пока либо из этих условий не выполняется:

поток, связанный с этим объектом QThread завершил выполнение (т.е. когда он возвращает из run()). Эта функция вернет true, если поток завершен. Он также возвращает true, если поток еще не запущен.

Истекшее время в миллисекундах. Если время ULONG_MAX (по умолчанию), то ожидание будет никогда не истечет время ожидания (поток должен возвратиться из run()). Эта функция вернет значение false, если ожидаемое время ожидания .

Если вам нужно синхронизировать два потока (ваша главная нить и создали нить), то я рекомендую использовать сигналы и слоты для сигнала, который один готов (вызвать isReady BOOL) и имеет while (!isReady) { sleep(1ms); processEvents(); } цикла происходит. Может быть, не лучший способ, но должен работать.

+0

Также верно о том, что говорят другие; Если вы используете только один набор кода за один раз, вам не нужен поток. – David

+0

Спасибо, Дэвид. Я добавил фрагмент кода, если вы не против взглянуть ... –

1

Я наконец-то нашел мой ответ здесь: http://comments.gmane.org/gmane.comp.lib.qt.user/6090

Короче говоря, если QThread :: бросить курить() вызываются как слот, обработчик цикла события создающего потока будет иметь дело с ним, что не то, что есть Я хочу.

Я должен назвать это напрямую. Так что, когда workerObject заканчивает свою работу, вместо того, чтобы посылать сигнал (который должен пройти через блокированную создающую нить), он должен непосредственно вызывать его контейнер бросить:

this->thread()->quit(); 

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

// Stop mechanism 
QObject::connect(workerObject, SIGNAL(finished()), workerThread, SLOT(quit())); 

Есть ли проблемы с этим подходом?

+0

Извините за поздний ответ, но вы хотите, чтобы этот сигнал был таким, что когда метод MyWorkerObject'run() выполняется, он испускает 'finished()' и поток класс очищает себя. По сути, единственное, что вы делаете с 'this-> thread() -> quit()', - это уничтожение потока, и метод запуска вашего 'MyWorkerObject' будет завершен без очистки. Вам будет лучше иметь слот, который вызывает логическое значение, которое сообщает '' MyWorkerObject'' Run() 'метод завершить, а затем, когда это завершает' finished() ', вызывается и« workThread »также очищается автоматически. – David

+0

Итак, у вас будет 'workerObject :: quit() {m_abort = true; } 'и будет вызываться так:' workerObject-> quit(); workerThread-> Join() ' – David

Смежные вопросы