对QTcpSocket对象的并发访问

Concurrent access to a QTcpSocket object

本文关键字:并发 访问 对象 QTcpSocket      更新时间:2023-10-16

我创建了一个从QThread继承的类,以便让线程与QTcpSocket对象一起运行。当这个线程启动时,它会连接到服务器:如果连接被拒绝或连接丢失,线程会尝试重新连接到服务器,尝试无限次重新连接。换句话说,这个线程试图保持与指定服务器的连接。

QTcpSocket*被声明为我的类的属性成员_socketrun()函数的第一行实例化_socket对象并尝试连接到服务器。run()函数的最后一行调用_socket->disconnectFromHost()。我注册了_socket对象断开连接的事件,以便调用_socket->deleteLater()

我创建的这个线程工作正常。现在我要添加一个函数来向服务器发送数据:这个函数应该调用_socket对象的write()函数,并且应该由另一个线程调用。那么,我应该使用互斥来使用_socket对象吗?

从QThread继承的类

首先解决这个问题。这里的很多人会告诉你你做错了!

QThread更像是一个线程控制器,而不是一个线程,所以除非你想改变Qt管理线程的方式,否则我建议你不要从它继承。相反,按照how to Really,Truly Use QThread中描述的方法。

我假设您有充分的理由在单独的线程中使用QTCpSocket,即使它是异步的。

如果您在另一个线程上有一个QTcpSocket,并且想要调用套接字的写函数,则应该使用信号/插槽机制,而不是直接从另一个螺纹调用对象的函数。

总之,在阅读了关于如何使用QThread的优秀文章之后,重构代码以创建一个独立的对象,该对象派生自QObject,可以移动到一个新的线程。然后使用signal/slot机制将数据发送到该对象,然后该对象可以调用套接字的写函数。

如果我理解正确,您希望从主线程向工作线程发送和接收信号。文档很好地解释了如何做到这一点:

class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork(const QString &parameter) {
        QString result;
        /* ... here is the expensive or blocking operation ... */
        emit resultReady(result);
    }
signals:
    void resultReady(const QString &result);
};
class Controller : public QObject
{
    Q_OBJECT
    QThread workerThread;
public:
    Controller() {
        Worker *worker = new Worker;
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller::operate, worker, &Worker::doWork);
        connect(worker, &Worker::resultReady, this, &Controller::handleResults);
        workerThread.start();
    }
    ~Controller() {
        workerThread.quit();
        workerThread.wait();
    }
public slots:
    void handleResults(const QString &);
signals:
    void operate(const QString &);
};

您必须创建一个QObject,它将有一个成员函数来执行您希望在单独线程中执行的操作。您将此QObject移动到QThread,以便在更改其相关性时执行操作,并向其发送/从其接收信号。

在这种情况下,没有必要也不建议继承QThread。但是,如果这样做,不要忘记在run()成员函数中调用exec(),这样线程就可以启动自己的事件循环(这样它就可以处理异步操作)。

所以不,您的操作不需要互斥。主线程和工作线程的事件循环可以很好地进行通信、相互发送信号和同步。

我建议您看看这个Consume/Productor解决方案,它同步2个线程,由Bradley Hughes在2006年创建,当时QThread::run()默认情况下没有启动事件循环。以下是他的例子的更新版本:

#include <QtCore>
#include <stdio.h>
enum {
    Limit = 123456,
    BlockSize = 7890
};
class Producer : public QObject
{
    Q_OBJECT
    QByteArray data;
    int bytes;
public:
    inline Producer() : bytes(0) { }
public slots:
    void produce()
    {
        int remaining = Limit - bytes;
        if (remaining == 0) {
            emit finished();
            return;
        }
        // this will never happen
        if (data.size() != 0)
            qFatal("Producer: Consumer failed to consume!");
        int size = qMin(int(BlockSize), remaining);
        bytes += size;
        remaining -= size;
        data.fill('Q', size);
        printf("Producer: produced %d more bytes, %d of %d totaln", size, bytes, Limit);
        emit produced(&data);
    }
signals:
    void produced(QByteArray *data);
    void finished();
};
class Consumer : public QObject
{
    Q_OBJECT
    int bytes;
public:
    inline Consumer() : bytes(0) { }
public slots:
    void consume(QByteArray *data)
    {
        // this will never happen
        if (data->size() == 0)
            qFatal("Consumer: Producer failed to produce!");
        int remaining = Limit - bytes;
        int size = data->size();
        remaining -= size;
        bytes += size;
        data->clear();
        printf("Consumer: consumed %d more bytes, %d of %d totaln", size, bytes, Limit);
        emit consumed();
        if (remaining == 0)
            emit finished();
    }
signals:
    void consumed();
    void finished();
};
int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    // create the producer and consumer and plug them together
    Producer producer;
    Consumer consumer;
    producer.connect(&consumer,
                     SIGNAL(consumed()),
                     SLOT(produce()));
    consumer.connect(&producer,
                     SIGNAL(produced(QByteArray *)),
                     SLOT(consume(QByteArray *)));
    // they both get their own thread
    QThread producerThread;
    producer.moveToThread(&producerThread);
    QThread consumerThread;
    consumer.moveToThread(&consumerThread);
    // start producing once the producer's thread has started
    producer.connect(&producerThread,
                     SIGNAL(started()),
                     SLOT(produce()));
    // when the consumer is done, it stops its thread
    consumerThread.connect(&consumer,
                           SIGNAL(finished()),
                           SLOT(quit()));
    // when consumerThread is done, it stops the producerThread
    producerThread.connect(&consumerThread,
                           SIGNAL(finished()),
                           SLOT(quit()));
    // when producerThread is done, it quits the application
    app.connect(&producerThread,
                SIGNAL(finished()),
                SLOT(quit()));
    // go!
    producerThread.start();
    consumerThread.start();
    return app.exec();
}
#include "main.moc"

阅读此处了解更多信息。