Blocage de l'expéditeur
Explique comment utiliser l'API synchrone de QSerialPort dans un thread de travail.
«Blocking Sender » montre comment créer une application pour une interface série à l'aide de l'API synchrone de QSerialPort dans un thread de travail.

QSerialPort prend en charge deux alternatives de programmation :
- L'alternative asynchrone (non bloquante). Les opérations sont planifiées et exécutées lorsque le contrôle revient à la boucle d'événements Qt. La classe QSerialPort émet un signal lorsque l'opération est terminée. Par exemple, la méthode write() renvoie immédiatement. Lorsque les données sont envoyées vers le port série, la classe QSerialPort émet le signal bytesWritten().
- L'alternative synchrone (bloquante). Dans les applications sans interface graphique et multithread, la méthode `wait` peut être appelée (dans ce cas, ` waitForReadyRead()`) pour suspendre le thread appelant jusqu’à ce que l’opération soit terminée.
Cet exemple illustre l’alternative synchrone. L’exemple Terminal illustre l’alternative asynchrone.
Le but de cet exemple est de montrer comment simplifier votre code de programmation série sans perdre la réactivité de l’interface utilisateur. L’API de programmation série bloquante conduit souvent à un code plus simple, mais elle ne doit être utilisée que dans des threads non liés à l’interface graphique afin de préserver la réactivité de celle-ci.
Cette application est l’émetteur qui illustre le fonctionnement en tandem avec l’application réceptrice de l’exemple « Blocking Receiver ».
L’application émettrice lance la requête de transfert via le port série vers l’application réceptrice et attend la réponse.
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 est une sous-classe de ` QThread ` qui fournit une API permettant de planifier les requêtes vers le récepteur. Cette classe fournit des signaux pour répondre et signaler les erreurs. La méthode `transaction()` peut être appelée pour lancer une nouvelle transaction d’émission avec la requête souhaitée. Le résultat est fourni par le signal `response()`. En cas de problème, le signal `error()` ou `timeout()` est émis.
Notez que la méthode `transaction()` est appelée dans le thread principal, mais que la requête est traitée dans le thread `SenderThread`. Les membres de données de `SenderThread` sont lus et écrits simultanément dans différents threads ; c’est pourquoi la classe ` QMutex ` est utilisée pour synchroniser l’accès.
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();
}La méthode transaction() stocke le nom du port série, le délai d’expiration et les données de requête. Le mutex peut être verrouillé à l’aide de QMutexLocker afin de protéger ces données. Le thread peut alors être lancé, à moins qu’il ne soit déjà en cours d’exécution. La méthode wakeOne() sera abordée plus loin.
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();Dans la fonction `run()`, la première étape consiste à verrouiller l’objet ` QMutex `, puis à récupérer le nom du port série, le délai d’expiration et les données de requête à l’aide des données membres. Une fois cette opération effectuée, le verrou ` QMutex ` est libéré.
La méthode ` transaction() ` ne doit en aucun cas être appelée simultanément avec un processus en cours de récupération des données. Notez que, bien que la classe ` QString ` soit réentrante, elle n’est pas thread-safe. Il n’est donc pas recommandé de lire le nom du port série dans un thread de requête, et de récupérer le délai d’expiration ou les données de la requête dans un autre thread. La classe `SenderThread` ne peut traiter qu’une seule requête à la fois.
L'objet ` QSerialPort ` est instancié sur la pile dans la méthode `run()` avant d'entrer dans la boucle :
QSerialPort serial;
if (currentPortName.isEmpty()) {
emit error(tr("No port name specified"));
return;
}
while (!m_quit) {Cela permet de créer un objet pendant l'exécution de la boucle. Cela signifie également que toutes les méthodes de l'objet sont exécutées dans le périmètre de la méthode run().
À l’intérieur de la boucle, on vérifie si le nom du port série de la transaction en cours a changé ou non. S’il a changé, le port série est rouvert puis reconfiguré.
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;
}
}La boucle continuera à demander des données, à écrire sur le port série et à attendre jusqu’à ce que toutes les données soient transférées.
// write request
const QByteArray requestData = currentRequest.toUtf8();
serial.write(requestData);
if (serial.waitForBytesWritten(m_waitTimeout)) {Avertissement : comme pour le transfert bloquant, la méthode ` waitForBytesWritten()` doit être utilisée après chaque appel à la méthode ` write `. Cela permettra de traiter toutes les routines d’E/S à la place de la boucle d’événements Qt.
Le signal timeout() est émis si une erreur de délai d’attente survient lors du transfert des données.
} else {
emit timeout(tr("Wait write request timeout %1")
.arg(QTime::currentTime().toString()));
}Il y a un délai d’attente pour la réponse après une requête réussie, puis la lecture est effectuée à nouveau.
// 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);Avertissement : comme pour l'alternative bloquante, la méthode waitForReadyRead() doit être utilisée avant chaque appel à read(). Cela permettra de traiter toutes les routines d'E/S au lieu de laisser cette tâche à la boucle d'événements Qt.
Le signal timeout() est émis si une erreur de délai d’attente survient lors de la réception des données.
} else {
emit timeout(tr("Wait read response timeout %1")
.arg(QTime::currentTime().toString()));
}Lorsqu’une transaction s’est achevée avec succès, le signal response() contient les données reçues de l’application réceptrice :
emit this->response(response);Ensuite, le thread se met en veille jusqu’à l’arrivée de la transaction suivante. À son réveil, le thread lit les nouvelles données à l’aide des membres et relance la boucle depuis le début.
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();
}Exécution de l'exemple
Vous pouvez exécuter l’exemple à partir de :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l’exemple dans « Examples ». Pour plus d’informations, consultez Qt Creator: Tutoriel : Compiler et exécuter.
- Qt Extension for Visual Studio Code
Exécutez la commande « Qt: Open Qt examples » à partir de Command Palette, puis sélectionnez l’exemple dans la liste. Pour plus d’informations, consultez Qt Extension for Visual Studio Code: Tutoriel : Compiler et exécuter.
Voir également Terminal série et Récepteur bloquant.
© 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.