Bloqueo del remitente
Muestra cómo utilizar la API síncrona de QSerialPort en un subproceso de trabajador.
Blocking Sender muestra cómo crear una aplicación para una interfaz serie utilizando la API síncrona de QSerialPort en un hilo worker.

QSerialPort soporta dos alternativas de programación:
- La alternativa asíncrona (sin bloqueo). Las operaciones se programan y realizan cuando el control vuelve al bucle de eventos de Qt. La clase QSerialPort emite una señal cuando finaliza la operación. Por ejemplo, el método write() retorna inmediatamente. Cuando los datos se envían al puerto serie, la clase QSerialPort emite la señal bytesWritten().
- La alternativa síncrona (bloqueante). En aplicaciones headless y multihilo, se puede llamar al método wait (en este caso, waitForReadyRead()) para suspender el hilo llamante hasta que la operación se haya completado.
En este ejemplo, se muestra la alternativa síncrona. El ejemplo Terminal ilustra la alternativa asíncrona.
El propósito de este ejemplo es demostrar cómo simplificar su código de programación serie sin perder la capacidad de respuesta de la interfaz de usuario. La API de programación serie de bloqueo a menudo conduce a un código más simple, pero sólo debe utilizarse en hilos no GUI para mantener la respuesta de la interfaz de usuario.
Esta aplicación es el remitente que demuestra el trabajo emparejado con la aplicación receptora Ejemplo de receptor bloqueante.
La aplicación emisora inicia la solicitud de transferencia a través del puerto serie a la aplicación receptora y espera la respuesta.
class SenderThread : public QThread { Q_OBJECT public: explicit SenderThread(QObject *parent = nullptr); ~SenderThread(); void transaction(const QString &portName, int waitTimeout, const QString &request); signals: void response(const QString &s); void error(const QString &s); void timeout(const QString &s); private: void run() override; QString m_portName; QString m_request; int m_waitTimeout = 0; QMutex m_mutex; QWaitCondition m_cond; bool m_quit = false; };
SenderThread es una subclase de QThread que proporciona API para programar peticiones al receptor. Esta clase proporciona señales para responder e informar de errores. El método transaction() puede ser llamado para iniciar la nueva transacción del emisor con la petición deseada. El resultado es proporcionado por la señal response(). En caso de problemas, se emite la señal error() o timeout().
Nota, el método transaction() es llamado en el hilo principal, pero la petición es proporcionada en el hilo SenderThread. Los miembros de datos de SenderThread son leídos y escritos concurrentemente en diferentes hilos, por lo tanto la clase QMutex es usada para sincronizar el acceso.
void SenderThread::transaction(const QString &portName, int waitTimeout, const QString &request) { const QMutexLocker locker(&m_mutex); m_portName = portName; m_waitTimeout = waitTimeout; m_request = request; if (!isRunning()) start(); else m_cond.wakeOne(); }
El método transaction() almacena el nombre del puerto serie, el tiempo de espera y los datos de la petición. El mutex puede ser bloqueado con QMutexLocker para proteger estos datos. El hilo puede iniciarse entonces, a menos que ya esté en ejecución. El método wakeOne() se discute más adelante.
void SenderThread::run() { bool currentPortNameChanged = false; m_mutex.lock(); QString currentPortName; if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } int currentWaitTimeout = m_waitTimeout; QString currentRequest = m_request; m_mutex.unlock();
En la función run(), lo primero es bloquear el objeto QMutex, luego obtener el nombre del puerto serie, el tiempo de espera y los datos de la petición utilizando los datos del miembro. Una vez hecho esto, se libera el bloqueo de QMutex.
Bajo ninguna circunstancia se debe llamar al método transaction() simultáneamente con un proceso que obtiene los datos. Tenga en cuenta que, aunque la clase QString es reentrante, no es segura para los hilos. Por lo tanto, no se recomienda leer el nombre del puerto serie en un hilo de solicitud, y el tiempo de espera o la solicitud de datos en otro hilo. La clase SenderThread sólo puede manejar una petición a la vez.
El objeto QSerialPort se construye en la pila en el método run() antes de entrar en el bucle:
QSerialPort serial; if (currentPortName.isEmpty()) { emit error(tr("No port name specified")); return; } while (!m_quit) {
Esto hace posible crear un objeto mientras se ejecuta el bucle. También significa que todos los métodos del objeto se ejecutan en el ámbito del método run().
Dentro del bucle se comprueba si el nombre del puerto serie de la transacción actual ha cambiado o no. Si ha cambiado, se vuelve a abrir el puerto serie y se reconfigura.
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; } }
El bucle continuará solicitando datos, escribiendo en el puerto serie y esperando hasta que todos los datos sean transferidos.
// write request const QByteArray requestData = currentRequest.toUtf8(); serial.write(requestData); if (serial.waitForBytesWritten(m_waitTimeout)) {
Advertencia: En cuanto a la transferencia de bloqueo, el método waitForBytesWritten() debe ser utilizado después de cada llamada al método write. Esto procesará todas las rutinas de E/S en lugar del bucle de eventos de Qt.
La señal timeout() se emite si se produce un error de timeout durante la transferencia de datos.
} else { emit timeout(tr("Wait write request timeout %1") .arg(QTime::currentTime().toString())); }
Hay un período de espera para la respuesta después de una solicitud exitosa, y luego se vuelve a leer.
// read response if (serial.waitForReadyRead(currentWaitTimeout)) { QByteArray responseData = serial.readAll(); while (serial.waitForReadyRead(10)) responseData += serial.readAll(); const QString response = QString::fromUtf8(responseData); emit this->response(response);
Advertencia: En cuanto a la alternativa de bloqueo, el método waitForReadyRead() debe utilizarse antes de cada llamada a read(). Esto procesará todas las rutinas de E/S en lugar del bucle de eventos de Qt.
La señal timeout() se emite si se produce un error de timeout al recibir datos.
} else { emit timeout(tr("Wait read response timeout %1") .arg(QTime::currentTime().toString())); }
Cuando una transacción se ha completado con éxito, la señal response() contiene los datos recibidos de la aplicación receptora:
emit this->response(response);
Después, el hilo se va a dormir hasta que aparezca la siguiente transacción. El hilo lee los nuevos datos después de despertarse utilizando los miembros y ejecuta el bucle desde el principio.
m_mutex.lock(); m_cond.wait(&m_mutex); if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } else { currentPortNameChanged = false; } currentWaitTimeout = m_waitTimeout; currentRequest = m_request; 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 Receptor 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.