En esta página

Hilos y QObjects

QThread hereda de QObject. Emite señales para indicar que el hilo ha comenzado o terminado de ejecutarse, y también proporciona algunas ranuras.

Más interesante es que QObjects puede ser utilizado en múltiples hilos, emitir señales que invocan ranuras en otros hilos, y enviar eventos a objetos que "viven" en otros hilos. Esto es posible porque a cada hilo se le permite tener su propio bucle de eventos.

Reentrada de QObject

QObject es reentrante. La mayoría de sus subclases no GUI, como QTimer, QTcpSocket, QUdpSocket y QProcess, son también reentrantes, haciendo posible el uso de estas clases desde múltiples hilos simultáneamente. Ten en cuenta que estas clases están diseñadas para ser creadas y utilizadas desde un único subproceso; crear un objeto en un subproceso y llamar a sus funciones desde otro subproceso no garantiza que funcione. Hay tres restricciones a tener en cuenta:

  • El hijo de un QObject debe crearse siempre en el subproceso en el que se creó el padre. Esto implica, entre otras cosas, que nunca debes pasar el objeto QThread (this) como padre de un objeto creado en el hilo (ya que el propio objeto QThread fue creado en otro hilo).
  • Los objetos controlados por eventos sólo pueden utilizarse en un único hilo. Específicamente, esto se aplica al mecanismo del temporizador y a network module. Por ejemplo, no puedes iniciar un temporizador o conectar un socket en un hilo que no sea object's thread.
  • Debes asegurarte de que todos los objetos creados en un hilo se borran antes de borrar el QThread. Esto se puede hacer fácilmente creando los objetos en la pila en tu implementación de run().

Aunque QObject es reentrante, las clases GUI, especialmente QWidget y todas sus subclases, no son reentrantes. Sólo pueden ser utilizadas desde el hilo principal. Como se ha señalado anteriormente, QCoreApplication::exec() también debe ser llamado desde ese hilo.

En la práctica, la imposibilidad de utilizar las clases GUI en otros subprocesos que no sean el principal puede solucionarse fácilmente poniendo las operaciones que consumen tiempo en un subproceso trabajador separado y mostrando los resultados en pantalla en el subproceso principal cuando el subproceso trabajador haya terminado. Este es el enfoque utilizado para implementar el ejemplo de Mandelbrot y el ejemplo del Cliente de Fortuna Bloqueante.

En general, la creación de QObjects antes de QApplication no está soportada y puede llevar a fallos extraños al salir, dependiendo de la plataforma. Esto significa que las instancias estáticas de QObject tampoco están soportadas. Una aplicación mono o multi-hilo correctamente estructurada debería hacer que QApplication sea el primero en crearse, y el último en destruirse QObject.

Bucle de eventos por hilo

Cada hilo puede tener su propio bucle de eventos. El hilo inicial inicia su bucle de eventos usando QCoreApplication::exec(), o para aplicaciones GUI de un solo diálogo, a veces QDialog::exec(). Otros subprocesos pueden iniciar un bucle de eventos utilizando QThread::exec(). Al igual que QCoreApplication, QThread proporciona una función exit(int) y una ranura quit().

Un bucle de eventos en un hilo hace posible que el hilo utilice ciertas clases Qt no-GUI que requieren la presencia de un bucle de eventos (como QTimer, QTcpSocket, y QProcess). También hace posible conectar señales de cualquier hilo a ranuras de un hilo específico. Esto se explica con más detalle en la sección Señales y ranuras entre hilos.

Hilos, objetos y bucles de eventos

Se dice que una instancia de QObject vive en el subproceso en el que se crea. Los eventos de ese objeto son enviados por el bucle de eventos de ese subproceso. El hilo en el que vive un QObject está disponible usando QObject::thread().

La función QObject::moveToThread() cambia la afinidad de hilo para un objeto y sus hijos (el objeto no puede ser movido si tiene un padre).

Llamar a delete en un QObject desde un hilo que no sea el que posee el objeto (o acceder al objeto de otra forma) no es seguro, a menos que garantice que el objeto no está procesando eventos en ese momento. Utiliza QObject::deleteLater() en su lugar, y se enviará un evento DeferredDelete, que el bucle de eventos del hilo del objeto recogerá eventualmente. Por defecto, el hilo que posee un QObject es el hilo que crea el QObject, pero no después de que QObject::moveToThread() haya sido llamado.

Si no se está ejecutando ningún bucle de eventos, los eventos no se entregarán al objeto. Por ejemplo, si creas un objeto QTimer en un hilo pero nunca llamas a exec(), el QTimer nunca emitirá su señal timeout(). Llamar a deleteLater() tampoco funcionará. (Estas restricciones se aplican también al hilo principal).

Puedes enviar manualmente eventos a cualquier objeto en cualquier hilo y en cualquier momento utilizando la función segura para hilos QCoreApplication::postEvent(). Los eventos serán enviados automáticamente por el bucle de eventos del subproceso en el que se creó el objeto.

Los filtros de eventos están soportados en todos los hilos, con la restricción de que el objeto monitorizado debe vivir en el mismo hilo que el objeto monitorizado. Del mismo modo, QCoreApplication::sendEvent() (a diferencia de postEvent()) sólo se puede utilizar para enviar eventos a los objetos que viven en el hilo desde el que se llama a la función.

Acceso a subclases de QObject desde otros subprocesos

QObject y todas sus subclases no son thread-safe. Esto incluye todo el sistema de envío de eventos. Es importante tener en cuenta que el bucle de eventos puede estar entregando eventos a tu subclase QObject mientras estás accediendo al objeto desde otro hilo.

Si estás llamando a una función en una subclase QObject que no vive en el hilo actual y el objeto puede recibir eventos, debes proteger todo el acceso a los datos internos de tu subclase QObject con un mutex; de lo contrario, puedes experimentar bloqueos u otros comportamientos no deseados.

Como otros objetos, los objetos QThread viven en el hilo en el que el objeto fue creado - no en el hilo que se crea cuando se llama a QThread::run(). Generalmente no es seguro proporcionar slots en tu subclase QThread, a menos que protejas las variables miembro con un mutex.

Por otro lado, puedes emitir señales de forma segura desde tu implementación de QThread::run(), porque la emisión de señales es segura para los hilos.

Señales y ranuras entre hilos

Qt soporta estos tipos de conexión señal-ranura:

  • Auto Connection (por defecto) Si la señal se emite en el hilo con el que el objeto receptor tiene afinidad entonces el comportamiento es el mismo que la Conexión Directa. En caso contrario, el comportamiento es el mismo que el de la Conexión en cola".
  • Direct Connection La ranura se invoca inmediatamente, cuando se emite la señal. El slot se ejecuta en el thread del emisor, que no es necesariamente el thread del receptor.
  • Queued Connection El slot se invoca cuando el control vuelve al bucle de eventos del thread del receptor. La ranura se ejecuta en el subproceso del receptor.
  • Blocking Queued Connection La ranura es invocada como para la Conexión en Cola, excepto que el hilo actual se bloquea hasta que la ranura regresa.

    Nota: El uso de este tipo para conectar objetos en el mismo subproceso provocará un bloqueo.

  • Unique Connection El comportamiento es el mismo que el de la Conexión Automática, pero la conexión se realiza sólo si no duplica una conexión existente. Es decir, si la misma señal ya está conectada a la misma ranura para el mismo par de objetos, entonces la conexión no se realiza y connect() devuelve false.

El tipo de conexión puede especificarse pasando un argumento adicional a connect(). Ten en cuenta que usar conexiones directas cuando el emisor y el receptor viven en hilos diferentes no es seguro si se está ejecutando un bucle de eventos en el hilo del receptor, por la misma razón que llamar a cualquier función en un objeto que vive en otro hilo no es seguro.

QObject::connectEl propio () es seguro.

El ejemplo de Mandelbrot utiliza una conexión en cola para comunicarse entre un hilo trabajador y el hilo principal. Para evitar congelar el bucle de eventos del hilo principal (y, como consecuencia, la interfaz de usuario de la aplicación), todo el cálculo fractal de Mandelbrot se realiza en un hilo trabajador separado. El hilo emite una señal cuando termina de renderizar el fractal.

De forma similar, el ejemplo del Cliente de Fortuna Bloqueante utiliza un hilo separado para comunicarse con un servidor TCP de forma asíncrona.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.