QTcpSocket 在子线程执行测试笔记

来源:互联网 发布:房子短租软件 编辑:程序博客网 时间:2024/06/05 07:50

本文是在子线程成创建QTcpSocke对象,并且向本地服务端在不同时刻发送不同指令。网上教程有很多,但发现大多数开启子线程的方式都是子类化QThread,然后在run()函数中写入需要执行的代码,但有篇博客强烈批评了该做法,主要原因请看以下两篇博客。

  • you are doing it wrong

  • Qt 的线程与事件循环——可打印threadid进行观察槽函数到底是在哪个线程里执行,学习moveToThread的使用)

    另外也发现基于moveToThread方法来实现Tcp通讯的比较少,所以特意记录在此。
    首先定义子类化QObject,

#include <QtCore>#include <QTcpSocket>class Worker : public QObject{    Q_OBJECTpublic:    ~Worker()    {        if(tcp!=nullptr)        {            delete tcp;        }    }private:    QTcpSocket *tcp;    //int i;public:    void foo()    {        qDebug()<<"Worker::Test get called from?: "<<QThread::currentThreadId();    }public slots:    void Connect()    {        tcp = new QTcpSocket;        tcp->connectToHost("127.0.0.1",110);        tcp->waitForConnected();    }    void onSend1()    {        tcp->write("Hello");        tcp->waitForBytesWritten();              //子线程ID号          qDebug()<<"Worker::Send get called from?: "<<QThread::currentThreadId();    }    void onSend2()    {        tcp->write("Qt");        tcp->waitForBytesWritten();        //子线程ID号        qDebug()<<"Worker::Send get called from?: "<<QThread::currentThreadId();    }};

定义一个Dummy类用于与出现子线程:

class Dummy:public QObject{    Q_OBJECTsignals:    void sendConnect();public:    void Connect()    {        emit sendConnect();    }};

主函数:

#include "main.moc"int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    //主线程Id号    qDebug()<<"From main thread: "<<QThread::currentThreadId();    QThread t;    QTimer timer;    QTimer timer2;    Worker worker;    Dummy dm;    QObject::connect(&dm,SIGNAL(sendConnect()),&worker,SLOT(Connect()));    QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onSend1()));    QObject::connect(&timer2,SIGNAL(timeout()),&worker,SLOT(onSend2()));    //关键一步    worker.moveToThread(&t);    //线程开启    t.start();    //向worker 发送连接请求                 dm.Connect();    //猜猜下面这个函数在哪个线程执行?    dm.foo();    timer.start(1000);    timer2.start(1000);    return a.exec();}

在此需要注意一点,对象内定义的成员变量是属于定义该对象的线程的,意思是Worker是在main()定义,那么Worker中定义的成员变量是属于主线程的,在其他slot函数中使用是属于跨线程使用。因此,如果在Worker的构造函数中对QTcpSocket进行实例化,那么执行上述方式的话,就会出现以下错误:

 QObject: Cannot create children for a parent that is in a different thread.(Parent is QTcpSocket(0x111ba20), parent's thread is QThread(0x11117f0), current thread is QThread(0x68fe68)QObject: Cannot create children for a parent that is in a different thread.(Parent is QTcpSocket(0x111ba20), parent's thread is QThread(0x11117f0), current thread is QThread(0x68fe68)

错误实例化成员变量

所以在Conntect() 在对QTcpSocket进行了实例化,并通过Workder的析构函数回收其内存,防止内存泄漏。

正确运行结果如下图所示:
正确运行结果

从结果可以看到,Dummy::foo() 实际上是在主线程执行的,而通过信号/槽机制调用的Dummy的slot函数是在子线程中执行的。

个人总结

接触Qt时间很短,信号/槽机制、以及事件并不是特别熟悉,既然这个方式是正确的,就先学着吧。而这个方法可以看做是QThread对象开启了一个线程,然后可以将QObject的子类对象放进这个线程中,如果需要调用QObject子类对象的函数,需要通过信号/槽机制来实现。

1 0
原创粉丝点击