0

我正在尝试修改 Qt Fortune Threaded Server 示例以从连接中读取文本,然后将其回显。我在我的 FortuneThread.h 文件中定义了 tcpSocket,如下所示:

QTcpSocket tcpSocket;

我的线程新运行函数如下所示:

void FortuneThread::run()
{
    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket.error());
        return;
    }
    connect(&tcpSocket, SIGNAL(readyREAD()), this, SLOT(readCommand()) );
}

哪个编译并运行,但是一旦我连接我得到这个错误(指连接线):

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x1eeb920), parent's thread is QThread(0x1bb3f90), current thread is FortuneThread(0x1eeb8f0)
QObject::connect: No such signal QTcpSocket::readyREAD() in ../fortune/fortunethread.cpp:60

有人可以向我解释原因吗?由于 tcpSocket 是在 FortuneThread 类(作为单独的线程运行)中定义的,并且“this”指的是 FortuneThread,我假设两个对象都在线程内?如何解决这个问题?

4

3 回答 3

4

您的套接字对象已在主线程中创建,但您正在从不同的线程访问它。您需要在线程的run()方法中创建它。定义套接字的位置无关紧要。当 C++ 运行时库进行静态对象初始化时,它将从主线程创建。

QTcpSocket * tcpSocket;

...

void FortuneThread::run() {
  tcpSocket = new QTcpSocket;
  ...
}
于 2013-09-27T03:19:02.663 回答
2

我同意库巴奥伯的观点。您应该阅读关于 Qt 线程、对象和事件的精彩指南。特别是,称为Signals and slots across threads的部分。作者建议将控制器和工作部分拆分为不同的本质。

代码中的第二个问题 - 区分大小写的信号名称。将其更改为readyRead.

于 2013-09-27T06:35:50.320 回答
1

Qt Fortune Threaded Server 示例的一个问题是它使用线程的方式。正如 Qt 的开发者所说,“你做错了”

问题是QThread的继承。QThread 类实际上不是线程,而是线程控制器类,继承它的唯一原因是如果您想更改控制线程的行为。

您看到的问题是由于线程亲和力;对象属于哪个线程。

如果一个线程是这样继承的:-

class FortuneThread : public QThread
{
    Q_OBJECT

    private:
        QTcpSocket tcpSocket;
};

然后从主线程实例化一个 FortuneThread 的对象:-

FortuneThread* ft = new FortuneThread(parent);

线程及其实例化的对象 (tcpSocket) 的线程亲和性现在是主线程,因此 tcpSocket 正在主线程上运行,这就是错误的说明。在调用 run 函数时,连接来自 FortuneThread,但 tcpSocket 在主线程上。

解决此问题的更好方法是创建从 QObject 派生的类并将其移动到线程:-

// Inherit from QObject, not QThread
class FortuneSocket : public QObject
{
    Q_OBJECT

    public slots:
       void Run();
    private:
        QTcpSocket tcpSocket;
};


QThread* pThread = new QThread(parent);
FortuneSocket* pFortune = new FortuneSocket(parent);

connect(pThread, &QThread::started, pFortune, &FortuneSocket::Run); // Qt5 connect style

// move the fortune socket to the thread: -
pFortune->moveToThread(pThread);

现在,当您使用 pThread->start() 启动线程时,FortuneSocket 对象及其所有成员都在新线程上运行。

以这种方式使用线程还意味着您可以将多个对象移动到单个线程,而不是每个线程拥有一个对象。请注意,创建比 CPU 内核更多的线程是没有意义的!

最后,这里有一篇关于如何使用 QThread 的更深入的文章。

于 2013-09-27T08:15:30.423 回答