2013-03-07 4 views
1

Я читаю, что я должен использовать рабочий объект и перенести его в thread путем moveToThread, а не наследовать из QThread напрямую. Но я не могу найти решение, как остановить цикл в моем объектном работнике. Например, у меня есть тестовый цикл:QThread - как остановить работника?

void CollectionWorker::doWork() 
{ 
    for (int i = 0; i < 10; ++i) { 
     sleep(1); 
     emit ping(i); 
    } 
} 

Теперь я движущуюся этот объект в теме:

worker->moveToThread(mTh); 

Это работает нормально. Но когда я вызываю mTh.quit(), тогда поток ждет, пока цикл в doWork не закончится. Когда я наследую QThread напрямую, то в каждом цикле я могу проверить состояние потока и цикл break, когда thred завершен, но не знаю, как это сделать в рабочем объекте. Могу ли я просто создать некоторый флаг в рабочем объекте и переключить его из основного потока? Или, может быть, я могу найти владельца потока и проверить его статус? Или, может быть, лучше, прежде чем начать поток, установить указатель потока в рабочем объекте и затем проверить статус? Какое наилучшее решение для безопасного потока?

С уважением

ответ

2

Редактировать: Извините, я не понял ваш вопрос.

Существует несколько альтернатив. Вы можете создать некоторый флаг, и перед каждой итерацией цикла обработки вы проверяете, установлен ли флаг. Или, в случае, если ваши данные обработки в списке/очереди, возможно, вы могли бы сигнализировать о завершении процесса со специальным элементом конца данных.

+0

Да, я знаю это, но это не проблема. Что делать, если мой цикл «вечно». Я хотел бы остановить его с помощью somethig: for (int i = 0; i <10; ++ i) { if (<конец завершено>) return; сон (1); испускать ping (i); } – Dibo

+1

Флаг имеет наибольший смысл.'while (stillDoingWork && notStopped)' – Anthony

+0

Установлен ли флаг для рабочего из основного потока (например, из кнопки clik) потокобезопасным? Только основной поток пишите на этот флаг, рабочий будет читать только – Dibo

0

идиоматический способ Qt, чтобы уничтожить рабочий поток с использованием интерфейса сигнала/слота:

Для того чтобы это работало CollectionWorker должны наследовать от класса QObject и объявить макрос Q_OBJECT.

4

Вызов quit() или exit() для объекта потока просто закончит цикл события потока, если есть какой-либо пробег. Но, как вы правильно указали, исходная проблема остается прежней. Что делать, если рабочая функция уже выполнена циклом события и является долговременной функцией с вечной конструкцией. Вызов quit() или exit() будет просто ждать, пока функция рабочего объекта не вернется.

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

  • Дайте сигнал завершения и слот в рабочем классе. Что-то вроде следующего.

    signals: 
     void signalTermination(); 
    public slots: 
     void setTerminationFlag(); 
    private: 
     QMutex mutex; 
     bool terminationRequested; 

Ваш слот будет выглядеть как-то.

void setTerminationFlag() 
{ 
    QMutexLocker locker(&mutex); 
    terminationRequested = true; 
} 

Затем вы можете проверить переменную в своей функции doWork на каждой итерации цикла forever.

mutex.lock(); 
if(terminationRequested) 
{ 
    //break from loop and effectively doWork function 
} 
mutex.unlock(); 

Одной из причин использования сигналов и слотов вместо обычной функции-члена является то, что, если ваша функция работник делает некоторые долго выполняющиеся задачи внутри синхронизированного блока кода, то общественная функция будет по-прежнему заблокирована до тех пор, пока не получит доступ объект синхронизации. Это может иметь отрицательный эффект, если вызывающий поток вашего общедоступного метода завершения - это поток пользовательского интерфейса.

  • Другой чистый и простой подход, если вы используете Qt 5.2 и далее метод

Использование requestInterruption() из QThread. Этот метод устанавливает контрольный флаг в объекте потока, который вы можете проверить с помощью вызова функции isInterruptionRequested() в вашей функции doWork(). См. Следующий фрагмент кода, указанный в документации QThread::isInterruptionRequested.

void long_task() { 
    forever { 
     if (QThread::currentThread()->isInterruptionRequested()) { 
      return; 
     } 
    } 
} 

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

+0

Ваше первое предложение, похоже, не работает для меня. Хотя мы используем сигналы, функция 'setTerminationFlag()' выполняется только после завершения длинного цикла, как описано для функций-членов. Второй вариант с 'isInterruptionRequested()' будет работать нормально, если бы не тот факт, что вы не можете сбросить этот флаг, если вы захотите немного сработать с ним позже. –

0

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

Ответы Чадика Робберта не помогли мне. Первое предложение вел себя так же, как то, что он описывает для функций-членов. То есть, хотя мы используем сигналы и слоты, вызывая setTerminationFlag слот из сигнала, испускаемого в основном потоке, правильно подключенного к нему, только запускается после окончания цикла. Но, возможно, я сделал что-то не так с реализацией. Если это действительно должно работать, сообщите мне, потому что это похоже на разблокировку сделки.

Его вторая альтернатива использования QThread::currentThread()->isInterruptionRequested(), чтобы остановить поток, прекратит работу, если бы не тот факт, что вы не можете сбросить этот флаг, если вы планируете повторно использовать поток и рабочий для некоторого аналогичного цикла интенсивной обработки , если не то же самое. Конечно, вы можете сделать это, остановив и запустив нить, но я не уверен, что это не будет иметь побочных эффектов, таких как очистка очередей выполнения. Этот вопрос был фактически размещен как bug report и Тиаго Macieira (ключ для разработчиков Qt) упоминает там, если я процитирую его:

Целью функции requestInterruption является, чтобы закончить нить.

Что делает requestInterruption неадекватным для работы, так как он сбрасывается только при запуске и окончании резьбы.

Решение, которое я нашел, что не кажется, что чистый для меня, чтобы включить QCoreApplication в классе рабочих и вызвать QCoreApplicaton::processEvents() время от времени, чтобы обработать эти поставленные в очередь сигналы в первом предложении Chadick Robbert или обновить класс класса осведомленности о переменных флага, разделяемых между потоками.

Поскольку вызова QCoreApplicaton::processEvents() внутри вашего цикла может замедлить его резко, что я делаю что-то вроде:

for(unsigned long int i=0; i<800000000 && !*stop; i++){ 
    f += (double)i * .001; // dummy calculation 
    if(i%10000000 == 0) // Call it only from time to time 
     QCoreApplication::processEvents(); // update *stop according to main Thread 
} 

Как вы можете видеть, с этим решением петля сломается только при целых кратных «10000000», которые могут не должны быть адекватными для некоторых случаев использования.

Если кто-нибудь знает об убийственном решении, которое я хотел бы услышать.

+0

Да. Первый вариант должен работать таким образом. Пожалуйста, обратите внимание на заявление в моем ответе «Тогда вы можете проверить переменную в вашей функции doWork в каждой итерации ** на бесконечном цикле». –

+0

Все дело в том, что вы хотите, чтобы поток завершил хотя бы текущее выполнение цикла, если мы хотим изящно завершить поток. Нет чистого способа получить прерывание в текущей строке цикла, если только ваш код не владеет насосом сообщений, и вы проверяете его перед выполнением каждого оператора. Как вы указали в своем собственном ответе, даже ваше решение будет работать только в кратных 10000000, но не в том случае, если я хочу закончить поток в счете 12345678. –

+0

Итак, если это действительно очень длинный цикл, создайте логические контрольные точки на шагах и проверьте или вызовите функцию 'isInturruptionRequested()'. Опять же, я просто предлагаю более чистые способы прекратить поток. –