Receptor de bloqueo
Muestra cómo utilizar la API síncrona de QSerialPort en un subproceso no GUI.
Blocking Receiver muestra cómo crear una aplicación para una interfaz serie utilizando la API síncrona de QSerialPort en un hilo no GUI.

QSerialPort admite dos enfoques generales de programación:
- El enfoque asíncrono (sin bloqueo). Las operaciones se programan y ejecutan cuando el control vuelve al bucle de eventos de Qt. QSerialPort emite una señal cuando la operación ha finalizado. Por ejemplo, QSerialPort::write() retorna inmediatamente. Cuando los datos se envían al puerto serie, QSerialPort emite bytesWritten().
- El enfoque síncrono (de bloqueo). En aplicaciones no GUI y multihilo, las funciones
waitFor...()pueden ser llamadas (por ejemplo, QSerialPort::waitForReadyRead()) para suspender el hilo de llamada hasta que la operación haya finalizado.
En este ejemplo, se muestra el enfoque síncrono. El ejemplo Terminal ilustra el enfoque asíncrono.
El propósito de este ejemplo es demostrar un patrón que puede utilizar para simplificar su código de programación serie, sin perder capacidad de respuesta en su interfaz de usuario. El uso de la API de programación serie de bloqueo de Qt a menudo conduce a un código más simple, pero debido a su comportamiento de bloqueo, sólo debe utilizarse en subprocesos no GUI para evitar que la interfaz de usuario se congele. Pero contrariamente a lo que muchos piensan, el uso de hilos con QThread no añade necesariamente una complejidad inmanejable a su aplicación.
Esta aplicación es un Receptor, que demostrar el trabajo emparejado con la aplicación remitente Bloqueo de remitente ejemplo.
La aplicación Receiver recibe la petición vía puerto serie de la aplicación Sender y le envía una respuesta.
Comenzaremos con la clase ReceiverThread, que maneja el código de programación serial.
class ReceiverThread : public QThread { Q_OBJECT public: explicit ReceiverThread(QObject *parent = nullptr); ~ReceiverThread(); void startReceiver(const QString &portName, int waitTimeout, const QString &response); signals: void request(const QString &s); void error(const QString &s); void timeout(const QString &s); private: void run() override; QString m_portName; QString m_response; int m_waitTimeout = 0; QMutex m_mutex; bool m_quit = false; };
ReceiverThread es una subclase de QThread que provee una API para recibir peticiones desde Sender, y tiene señales para entregar respuestas y reportar errores.
Usted debe llamar a startReceiver() para iniciar la aplicación Receiver. Este método transfiere al ReceiverThread los parámetros deseados para configurar e iniciar la interfaz serial. Cuando ReceiverThread recibe del Sender alguna solicitud entonces emite la señal request(). Si ocurre algún error, se emite la señal error() o timeout().
Es importante notar que startReceiver() es llamado desde el thread principal, GUI, pero los datos de respuesta y otros parámetros serán accedidos desde el thread de ReceiverThread. Los miembros de datos de ReceiverThread son leídos y escritos desde diferentes threads concurrentemente, por lo que es aconsejable utilizar QMutex para sincronizar el acceso.
void ReceiverThread::startReceiver(const QString &portName, int waitTimeout, const QString &response) { const QMutexLocker locker(&m_mutex); m_portName = portName; m_waitTimeout = waitTimeout; m_response = response; if (!isRunning()) start(); }
La función startReceiver() almacena el nombre del puerto serie, el tiempo de espera y los datos de respuesta, y QMutexLocker bloquea el mutex para proteger estos datos. A continuación, iniciamos el hilo, a menos que ya se esté ejecutando. QWaitCondition::wakeOne() se discutirá más adelante.
void ReceiverThread::run() { bool currentPortNameChanged = false; m_mutex.lock(); QString currentPortName; if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } int currentWaitTimeout = m_waitTimeout; QString currentRespone = m_response; m_mutex.unlock();
En la función run(), comienza adquiriendo el bloqueo mutex, obtiene el nombre del puerto serie, el tiempo de espera y los datos de respuesta de los datos miembros, y luego libera el bloqueo de nuevo. Bajo ninguna circunstancia el método startReceiver() debe ser llamado simultáneamente con un proceso obteniendo estos datos. QString es reentrante pero no seguro para hilos, y no es recomendable leer el nombre del puerto serie de un inicio, la llamada y el tiempo de espera o los datos de respuesta de otro. ReceiverThread solo puede manejar un inicio a la vez.
El objeto QSerialPort lo construimos en la pila en la función run() antes de entrar en el bucle:
QSerialPort serial; while (!m_quit) {
Esto nos permite crear una vez un objeto, mientras se ejecuta el bucle, y también significa que todos los métodos del objeto se ejecutarán en el contexto del hilo run().
En el bucle, comprueba si el nombre del puerto serie para el inicio actual ha cambiado o no. Si lo ha hecho, reabre y reconfigura el puerto serie.
if (currentPortName.isEmpty()) { emit error(tr("Port not set")); return; } else if (currentPortNameChanged) { serial.close(); serial.setPortName(currentPortName); if (!serial.open(QIODevice::ReadWrite)) { emit error(tr("Can't open %1, error code %2") .arg(m_portName).arg(serial.error())); return; } } if (serial.waitForReadyRead(currentWaitTimeout)) {
El bucle continuará esperando los datos de la petición:
// read request QByteArray requestData = serial.readAll(); while (serial.waitForReadyRead(10)) requestData += serial.readAll();
Advertencia: El método waitForReadyRead() debe usarse antes de cada llamada a read() para el enfoque de bloqueo, porque procesa todas las rutinas de E/S en lugar del bucle de eventos de Qt.
La señal timeout() se emite si ocurre un error al leer datos.
} else { emit timeout(tr("Wait read request timeout %1") .arg(QTime::currentTime().toString())); }
Después de una lectura exitosa, intenta enviar una respuesta y espera a que se complete la transferencia:
// write response const QByteArray responseData = currentRespone.toUtf8(); serial.write(responseData); if (serial.waitForBytesWritten(m_waitTimeout)) { const QString request = QString::fromUtf8(requestData); emit this->request(request);
Advertencia: El método waitForBytesWritten() debe usarse después de cada llamada a write() para el enfoque de bloqueo, porque procesa todas las rutinas de E/S en lugar de Qt event-loop.
La señal timeout() es emitida si ocurre un error al escribir datos.
} else { emit timeout(tr("Wait write response timeout %1") .arg(QTime::currentTime().toString())); }
Después de una escritura exitosa se emite la señal request() que contiene los datos recibidos de la aplicación Remitente:
emit this->request(request);
A continuación, el hilo pasa a leer los parámetros actuales de la interfaz serie, porque ya pueden haber sido actualizados, y ejecuta el bucle desde el principio.
m_mutex.lock(); if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } else { currentPortNameChanged = false; } currentWaitTimeout = m_waitTimeout; currentRespone = m_response; m_mutex.unlock(); }
Ejecución del ejemplo
Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para más información, ver Qt Creator: Tutorial: Construir y ejecutar.
Ver también Terminal serie y Emisor de bloqueo.
© 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.