Вот один пример того, как правильно использовать QThread , но у него есть некоторые проблемы, которые отражены в комментариях.В частности, поскольку порядок выполнения слотов строго не определен, это может привести к различным проблемам.Комментарий, опубликованный 6 августа 2013 года, дает хорошее представление о том, как бороться с этой проблемой.Я использую что-то подобное в моей программе, и вот несколько примеров кода для пояснения.
Основная идея та же: я создаю экземпляр QThread, который живет в моем основном потоке, экземпляр рабочего класса, который живет вновый поток, который я создал, а затем подключаю все сигналы.
void ChildProcesses::start()
{
QThread *childrenWatcherThread = new QThread();
ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
childrenWatcher->moveToThread(childrenWatcherThread);
// These three signals carry the "outcome" of the worker job.
connect(childrenWatcher, SIGNAL(exited(int, int)),
SLOT(onChildExited(int, int)));
connect(childrenWatcher, SIGNAL(signalled(int, int)),
SLOT(onChildSignalled(int, int)));
connect(childrenWatcher, SIGNAL(stateChanged(int)),
SLOT(onChildStateChanged(int)));
// Make the watcher watch when the thread starts:
connect(childrenWatcherThread, SIGNAL(started()),
childrenWatcher, SLOT(watch()));
// Make the watcher set its 'stop' flag when we're done.
// This is performed while the watch() method is still running,
// so we need to execute it concurrently from this thread,
// hence the Qt::DirectConnection. The stop() method is thread-safe
// (uses a mutex to set the flag).
connect(this, SIGNAL(stopped()),
childrenWatcher, SLOT(stop()), Qt::DirectConnection);
// Make the thread quit when the watcher self-destructs:
connect(childrenWatcher, SIGNAL(destroyed()),
childrenWatcherThread, SLOT(quit()));
// Make the thread self-destruct when it finishes,
// or rather, make the main thread delete it:
connect(childrenWatcherThread, SIGNAL(finished()),
childrenWatcherThread, SLOT(deleteLater()));
childrenWatcherThread->start();
}
Некоторый фон:
Класс ChildProcesses - это дочерний менеджер процессов, который запускает новые дочерние процессы с помощью вызовов spawn (),хранит список запущенных процессов и так далее.Тем не менее, он должен отслеживать дочерние состояния, что означает использование waitpid () в Linux или WaitForMultipleObjects в Windows.Раньше я вызывал их в неблокирующем режиме, используя таймер, но теперь я хочу более быстрой реакции, что означает режим блокировки.Вот где начинается поток.
Класс ChildrenWatcher определяется следующим образом:
class ChildrenWatcher: public QObject {
Q_OBJECT
private:
QMutex mutex;
bool stopped;
bool isStopped();
public:
ChildrenWatcher();
public slots:
/// This is the method which runs in the thread.
void watch();
/// Sets the stop flag.
void stop();
signals:
/// A child process exited normally.
void exited(int ospid, int code);
/// A child process crashed (Unix only).
void signalled(int ospid, int signal);
/// Something happened to a child (Unix only).
void stateChanged(int ospid);
};
Вот как это работает.Когда все это запускается, вызывается метод ChildProcess :: start () (см. Выше).Он создает новый QThread и новый ChildrenWatcher, который затем перемещается в новый поток.Затем я подключаю три сигнала, которые информируют моего менеджера о судьбе его дочерних процессов (выход / сигнал / бог знает, что случилось).Затем начинается основное удовольствие.
Я подключаю QThread :: start () к методу ChildrenWatcher :: watch (), чтобы он запускался, как только поток готов.Поскольку наблюдатель живет в новом потоке, именно здесь выполняется метод watch () (подключение к очереди используется для вызова слота).
Затем я подключаю сигнал ChildProcesses :: stop () к ChildrenWatcher:: stop (), используя Qt :: DirectConnection, потому что мне нужно сделать это асинхронно.Это необходимо, чтобы мой поток останавливался, когда диспетчер ChildProcesses больше не нужен.Метод stop () выглядит следующим образом:
void ChildrenWatcher::stop()
{
mutex.lock();
stopped = true;
mutex.unlock();
}
А затем ChildrenWatcher :: watch ():
void ChildrenWatcher::watch()
{
while (!isStopped()) {
// Blocking waitpid() call here.
// Maybe emit one of the three informational signals here too.
}
// Self-destruct now!
deleteLater();
}
Oh, а метод isStopped () - это просто удобный способиспользуйте мьютекс в условии while ():
bool ChildrenWatcher::isStopped()
{
bool stopped;
mutex.lock();
stopped = this->stopped;
mutex.unlock();
return stopped;
}
Итак, здесь происходит то, что я устанавливаю флаг остановки, когда мне нужно закончить, а затем при следующем вызове isStopped () он возвращает false ипоток заканчивается.
Так что же происходит, когда заканчивается цикл watch ()?Он вызывает deleteLater (), поэтому объект самоуничтожается, как только управление возвращается в цикл событий потока, который происходит сразу после вызова deleteLater () (когда возвращается watch ()).Возвращаясь к ChildProcesses :: start (), вы можете видеть, что существует связь от сигнала destroy () наблюдателя к слоту quit () потока.Это означает, что поток автоматически завершает работу, когда завершает работу наблюдатель.И когда он закончен, он также самоуничтожается, потому что его собственный сигнал finish () подключен к его слоту deleteLater ().
Это почти та же идея, что и в Maya, но я используюидиома, мне не нужно зависеть от последовательности, в которой вызываются слоты.Сначала он всегда самоуничтожается, потом останавливается, потом тоже самоуничтожается.Я мог бы определить сигнал done () в работнике, а затем подключить его к своему собственному deleteLater (), но это означало бы только одно соединение больше.Поскольку для каких-либо других целей мне не нужен сигнал finish (), я решил просто вызвать deleteLater () из самого работника.
Майя также упоминает, что вы не должны выделять новые объекты QObject в конструкторе работника, потому что они не будут жить в потоке, в который вы перемещаете работника. Я бы сказал, в любом случае, сделайте это, потому что так работает ООП. Просто убедитесь, что все эти объекты QObject являются потомками работника (то есть используйте конструктор QObject (QObject *)) - moveToThread () перемещает все дочерние объекты вместе с перемещаемым объектом. Если вам действительно нужно иметь объекты QObject, которые не являются дочерними для вашего объекта, переопределите moveToThread () в вашем работнике, чтобы он также перемещал все необходимые вещи.