【问题标题】:Concurrent access to a QTcpSocket object并发访问 QTcpSocket 对象
【发布时间】:2014-10-13 13:27:55
【问题描述】:

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

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

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

【问题讨论】:

    标签: c++ multithreading qt qthread qtcpsocket


    【解决方案1】:

    继承自 QThread 的类

    首先解决这个问题。这里很多人会告诉你You're Doing it Wrong!

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

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

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

    因此,总结一下,在阅读了如何使用 QThread 的excellent article 之后,重构您的代码以创建一个从 QObject 派生的单独对象,该对象可以移动到新线程。然后使用信号/槽机制向那个对象发送数据,然后这个对象就可以调用socket的write函数了。

    【讨论】:

      【解决方案2】:

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

      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,它有一个成员函数来执行您希望在单独线程中执行的操作。你 move 这个 QObjectQThread 你想要执行操作来改变它的 affinity 并发送/接收信号到/从它。

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

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

      我建议查看此Consume/Producer 解决方案,它同步 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 total\n", 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 total\n", 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"
      

      阅读here了解更多信息。

      【讨论】:

        猜你喜欢
        • 2011-10-17
        • 1970-01-01
        • 1970-01-01
        • 2020-09-09
        • 1970-01-01
        • 1970-01-01
        • 2012-03-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多