Aquí es one example of how to use QThread correctly, pero tiene algunos problemas con ella, que se reflejan en el comentarios En particular, dado que el orden en que se ejecutan las ranuras no está estrictamente definido, podría dar lugar a varios problemas. El comentario publicado el 6 de agosto de 2013 da una buena idea de cómo lidiar con este problema. Uso algo así en mi programa, y aquí hay un código de ejemplo para aclarar.
La idea básica es la misma: creo una instancia de QThread que vive en mi hilo principal, una instancia de clase trabajadora que vive en el nuevo hilo que creé, y luego conecto todas las señales.
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();
}
Algunos antecedentes:
clase Los ChildProcesses es un gestor de procesos hijo que se inicia nuevos procesos hijo con la freza() llama, mantiene la lista de los procesos que se ejecutan en la actualidad y así sucesivamente. Sin embargo, necesita realizar un seguimiento de los estados secundarios, lo que significa usar waitpid() llamada en Linux o WaitForMultipleObjects en Windows. Solía llamar a estos en modo sin bloqueo con un temporizador, pero ahora quiero una reacción más rápida, lo que significa modo de bloqueo. Ahí es donde el hilo entra en
La clase ChildrenWatcher se define como sigue:.
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);
};
Aquí cómo funciona. Cuando se inicia todo esto, se llama al método ChildProcess :: start() (ver arriba). Crea un nuevo QThread y un nuevo ChildrenWatcher, que luego se mueve al nuevo hilo. Luego conecto tres señales que informan a mi gerente sobre el destino de sus procesos secundarios (exit/signaled/god-knows-what-happened). Entonces comienza la diversión principal.
Conecto QThread :: started() al método ChildrenWatcher :: watch() por lo que se inicia tan pronto como el hilo está listo. Como el observador vive en el nuevo subproceso, ahí es donde se ejecuta el método watch() (la conexión en cola se usa para llamar a la ranura).
Luego conecto la señal ChildProcesses :: stopped() a la ranura ChildrenWatcher :: stop() usando Qt :: DirectConnection porque necesito hacerlo de forma asíncrona. Esto es necesario para que mi hilo se detenga cuando ya no se necesite el administrador ChildProcesses. El método stop() tiene el siguiente aspecto:
void ChildrenWatcher::stop()
{
mutex.lock();
stopped = true;
mutex.unlock();
}
Y luego ChildrenWatcher :: reloj():
void ChildrenWatcher::watch()
{
while (!isStopped()) {
// Blocking waitpid() call here.
// Maybe emit one of the three informational signals here too.
}
// Self-destruct now!
deleteLater();
}
Ah, y el método isStopped() es sólo una manera conveniente utilizar un mutex el tiempo() condición:
bool ChildrenWatcher::isStopped()
{
bool stopped;
mutex.lock();
stopped = this->stopped;
mutex.unlock();
return stopped;
}
Así que lo que sucede aquí es que me puse la bandera se detuvo cuando tengo que terminar, y entonces la próxima vez isStopped() se llama devuelve falso y termina el hilo.
Entonces, ¿qué sucede cuando termina el ciclo watch()? Llama a deleteLater() para que el objeto se autodestruya tan pronto como se devuelva el control al bucle de evento de la secuencia que ocurre justo después de la llamada deleteLater() (cuando vuelve a aparecer watch()). Volviendo a ChildProcesses :: start(), puede ver que hay una conexión desde la señal destroy() del vigilante hasta la ranura quit() del hilo. Esto significa que el hilo termina automáticamente cuando el observador está listo.Y cuando termina, se autodestruye también porque su propia señal de finalización() está conectada a su ranura deleteLater().
Esta es más o menos la misma idea que Maya publicó, pero como utilizo el modismo de autodestrucción, no necesito depender de la secuencia en que se llaman las ranuras. Siempre se autodestruye primero, detiene el hilo más tarde, luego se autodestruye también. Podría definir una señal finished() en el worker, y luego conectarla a su propia deleteLater(), pero eso solo significaría una conexión más. Como no necesito una señal terminada() para ningún otro propósito, elijo simplemente llamar a deleteLater() del propio trabajador.
Maya también menciona que no debe asignar nuevos QObjects en el constructor del trabajador porque no vivirán en el hilo al que mueve el trabajador. Yo diría que hacerlo de todos modos porque esa es la forma en que OOP funciona. Solo asegúrese de que todos esos QObjects sean hijos del trabajador (es decir, use el constructor QObject (QObject *)) - moveToThread() mueve todos los elementos secundarios junto con el objeto que se está moviendo. Si realmente necesita tener QObjects que no sean hijos de su objeto, entonces anule moveToThread() en su trabajador para que también mueva todo lo necesario.
Debe tenerse en cuenta que la nueva documentación en 4.8 establece la forma correcta de usar un QThread y desalienta activamente la antigua forma de Derivar de un objeto QThread solo para crear un objeto/subproceso de trabajo. http://qt-project.org/doc/qt-4.8/qthread.html#details – g19fanatic
El doc solo dice que no se recomienda introducir nuevas ranuras en la subclase QThread. No mencionó derivar de la clase QThread. Derivado de QThread sigue el mismo paradigma de TThread Delphi/C++ Builder. –
http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html –