QT基于UDP通信的多线程编程问题

近来用Qt编写一段多线程的TcpSocket通信程序,被其中Qt中报的几个warning搞晕了,一会儿是说“Cannot create children for a parent that is in a different thread”,有时候又是“QSocketNotifier: socket notifiers cannot be enabled from another thread”,还经常又Assert failure:Cannot send events toobjects owned by a different thread,从而导致程序崩溃。

为彻底搞清原因并解决问题,在查阅大量资料和Qt文档之后,理清了其中的机制,也对多线程编程中的QObject对象创建以及connect执行有更清楚的认识:

1. 一个对象的线程就是创建该对象时的线程,而不论该对象的定义是保存在那个线程中;

2. QObject的connect函数有几种连接方式,

a) DirectConnection,信号发送后槽函数立即执行,由sender的所在线程执行;

b) QueuedConnection,信号发送后返回,相关槽函数由receiver所在的线程在返回到事件循环后执行;

c) 默认使用的是Qt::AutoConnection,当sender和receiver在同一个线程内时,采用DirectConnection的方式,当sender和receiver在不同的线程时,采用QueuedConnection的方式。

为了更清楚的理解这些问题,在此特编了个小例子说明一下。首先定义一个从QObject继承的类SomeObject,包含一个信号someSignal和一个成员函数callEmitSignal,此函数用于发送前面的someSignal信号。定义如下:

// define Object class

  1. class SomeObject : public QObject  
  2. {  
  3.     Q_OBJECT  
  4. public:  
  5.     SomeObject(QObject* parent=0) : QObject(parent) {}  
  6.     void callEmitSignal()  // 用于发送信号的函数   
  7.     {  
  8.         emit someSignal();  
  9.     }  
  10. signals:  
  11.     void someSignal();  
  12. };


然后再定义一个从QThread继承的线程类SubThread,它包含一个SomeObject的对象指针obj,另外有一个slot函数someSolt,定义如下:

  1. class SubThread : public QThread  
  2. {  
  3.     Q_OBJECT  
  4. public:  
  5.     SubThread(QObject* parent=0) : QThread(parent){}  
  6.     virtual ~SubThread()  
  7.     {  
  8.         if (obj!=NULL) delete obj;  
  9.     }  
  10. public slots:  
  11.     // slot function connected to obj's someSignal   
  12.     void someSlot();  
  13. public:  
  14.     SomeObject * obj;  
  15. };  
  16. // slot function connected to obj's someSignal   
  17. void SubThread::someSlot()  
  18. {  
  19.     QString msg;  
  20.     msg.append(this->metaObject()->className());  
  21.     msg.append("::obj's thread is ");  
  22.     if (obj->thread() == qApp->thread())  
  23.     {  
  24.         msg.append("MAIN thread;");  
  25.     }  
  26.     else if (obj->thread() == this)  
  27.     {  
  28.         msg.append("SUB thread;");  
  29.     }  
  30.     else  
  31.     {  
  32.         msg.append("OTHER thread;");  
  33.     }  
  34.     msg.append(" someSlot executed in ");  
  35.     if (QThread::currentThread() == qApp->thread())  
  36.     {  
  37.         msg.append("MAIN thread;");  
  38.     }  
  39.     else if (QThread::currentThread() == this)  
  40.     {  
  41.         msg.append("SUB thread;");  
  42.     }  
  43.     else  
  44.     {  
  45.         msg.append("OTHER thread;");  
  46.     }  
  47.     qDebug() << msg;  
  48.     quit();  
  49. }

这里someSlot函数主要输出了obj所在的线程和slot函数执行线程。

接着从SubThread又继承了3个线程类,分别是SubThread1, SubThread2, SubThread3.分别实现线程的run函数。定义如下:

  1. // define sub thread class 1   
  2. class SubThread1 : public SubThread  
  3. {  
  4.     Q_OBJECT  
  5. public:  
  6.     SubThread1(QObject* parent=0);  
  7.     // reimplement run   
  8.     void run();  
  9. };  
  10. class SubThread2 : public SubThread  
  11. {  
  12.     Q_OBJECT  
  13. public:  
  14.     SubThread2(QObject* parent=0);  
  15.     // reimplement run   
  16.     void run();  
  17. };  
  18. class SubThread3 : public SubThread  
  19. {  
  20.     Q_OBJECT  
  21. public:  
  22.     SubThread3(QObject* parent=0);  
  23.     // reimplement run   
  24.     void run();  
  25. };

在主程序中分别创建3个不同的线程并运行,查看运行结果。

  1. int main(int argc, char *argv[])  
  2. {  
  3.     QCoreApplication a(argc, argv);  
  4.     SubThread1* t1 = new SubThread1(&a); //由主线程创建   
  5.     t1->start();  
  6.     SubThread2* t2 = new SubThread2(&a); //由主线程创建   
  7.     t2->start();  
  8.     SubThread3* t3 = new SubThread3(&a); //由主线程创建   
  9.     t3->start();  
  10.     return a.exec();  
  11. }

下面我们来分析不同写法的程序,其obj对象所在的线程空间和someSlot函数执行的线程空间分别是怎样的。

首先看SubThread1的实现:

  1.   
  2. // class SubThread1   
  3.   
  4. SubThread1::SubThread1(QObject* parent)  
  5.     : SubThread(parent)  
  6. {  
  7.     obj = new SomeObject();//由主线程创建   
  8.     connect(obj, SIGNAL(someSignal()), this, SLOT(someSlot()));  
  9. }  
  10. // reimplement run   
  11. void SubThread1::run()  
  12. {  
  13.     obj->callEmitSignal();  
  14.     exec();  
  15. }

可以看到,obj是在构造函数中被创建的,那么创建obj对象的线程也就是创建SubThread1的线程,一般是主线程,而不是SubThread1所代表的线程。同时由于obj和this(即t1)都位于主线程,所以someSlot函数也是由主线程来执行的。

而在线程SubThread2中,我们把obj对象的创建放到子线程的run函数中,那么obj对象的线程就应该SubThread2代表的线程,即t2,就不再是主线程了。

  1.   
  2. // class SubThread2   
  3.   
  4. SubThread2::SubThread2(QObject* parent)  
  5.     : SubThread(parent)  
  6. {  
  7.     obj=0;  
  8. }  
  9. // reimplement run   
  10. void SubThread2::run()  
  11. {  
  12.     obj = new SomeObject(); //由当前子线程创建   
  13.     connect(obj, SIGNAL(someSignal()), this, SLOT(someSlot()));  
  14.     obj->callEmitSignal();  
  15.     exec();  
  16. }

同时,在connect函数中由于obj和this(这里是t2)不是在同一个线程中,因此会采用QueuedConnection的方式,其slot函数由this对象所在的线程即主线程来执行。这里有一个特别容易误解的地方,就是这个slot函数虽然是子线程SubThread2的一个成员函数,connect操作也是在子线程内完成的,但是该函数的执行却不在子线程内,而是在主线程内。

那么如果想让相应的slot函数在子线程内执行,该如何做呢?在子线程的run函数中创建obj对象的同时,在执行connect时指定连接方式为DirectConnection,这样就可以使slot函数在子线程中运行,因为DirectConnection的方式始终由sender对象的线程执行。如

  1.   
  2. // class SubThread3   
  3.   
  4. SubThread3::SubThread3(QObject* parent)  
  5.     : SubThread(parent)  
  6. {  
  7.     obj=0;  
  8. }  
  9. // reimplement run   
  10. void SubThread3::run()  
  11. {  
  12.     obj = new SomeObject();  
  13.     connect(obj, SIGNAL(someSignal()), this, SLOT(someSlot()),  
  14.             Qt::DirectConnection);  
  15.     obj->callEmitSignal();  
  16.     exec();  
  17. }

最后,该程序的运行结果应该是:

  1. "SubThread1::obj's thread is MAIN thread; someSlot executed in MAIN thread;"   
  2. "SubThread2::obj's thread is SUB thread; someSlot executed in MAIN thread;"   
  3. "SubThread3::obj's thread is SUB thread; someSlot executed in SUB thread;"

  • 6
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: UDP(User Datagram Protocol)是一种面向无连接的传输协议,它在传输数据时不需要建立连接,因此传输速度较快。Qt是一种功能强大的开发框架,支持多种操作系统和平台。在Qt工程中使用多线程高速接收UDP数据包,可以提高数据传输效率,加快数据处理速度,提升应用程序的运行效率。 在Qt工程中可以使用多线程实现高速接收UDP数据包的功能,每个线程可以处理一个或多个数据流,同时将数据流传递给主线程进行处理。多线程的实现提高了数据接收的效率,避免了数据包接收过程中的阻塞等问题。 在Qt工程中,可以使用QThread类来实现多线程功能。各个线程之间的数据共享可以通过Qt的信号和槽机制实现。当有数据包到达时,线程将收到相应的信号,然后调用槽函数将数据包传递给主线程进行处理。主线程可以使用Qt提供的相关类(如QUdpSocket)来对UDP数据包进行解码和处理,最终将数据传递给应用程序的其他模块进行进一步的处理。 在实现多线程高速接收UDP数据包的过程中,需要注意数据的处理顺序和线程之间的同步问题。同时需要对UDP数据包进行合理的分组和缓存,以避免数据包阻塞和丢失问题的发生。 综上所述,基于UDP协议的多线程高速接收Qt工程可以通过多线程的方式实现数据的快速接收和处理,提高应用程序的运行效率和性能。 ### 回答2: 基于UDP协议的多线程高速接收QT工程是一个基于QT框架开发的网络应用程序,运用UDP协议来实现多个客户端与服务端之间的数据通信。多线程的应用可以更好地利用CPU资源,提高系统的并发性能,从而更快地响应用户请求。 在该工程中,服务端通过UDP协议接收来自多个客户端的数据包,并将数据包进行处理,处理完成后再将结果返回给客户端。多线程的应用可以使得服务端同时接收和处理多个客户端的请求,提高服务端的性能。 在QT框架下,实现多线程的方式有很多种,比如QThread类、QtConcurrent库、QtConcurrent::run等。在开发该工程时,可以根据具体情况选择合适的多线程实现方式。 另外,在UDP协议的通信过程中,由于不保证数据传输的可靠性,在客户端与服务端之间进行数据传输时需要考虑到数据丢失、重复等问题,可以通过一系列的技术手段来解决,比如差错控制、帧同步等。 总之,基于UDP协议的多线程高速接收QT工程是一个能够实现多个客户端与服务端之间高速数据传输的网络应用程序,可广泛应用于音视频、游戏、工业控制等领域。 ### 回答3: UDP协议是一种无连接的协议,它优秀的性能和简单的结构使得它成为实时应用中通信的流行选择。基于UDP协议的多线程高速接收Qt工程,涉及到以下几个方面。 首先,Qt提供了多线编程的支持。通过创建多个线程,可以同时接收多个UDP数据包,从而提高接收速度。在Qt中,可以使用QThread,QThreadPool和QRunnable等类来实现多线编程。 其次,对于接收到的UDP数据包,需要对其进行处理,并将处理结果反馈给用户。在Qt中,可以通过信号和槽机制来实现数据包处理与用户反馈的交互。具体地,可以在每个处理线程中创建一个QObject子类,用于处理接收到的数据包,并通过信号和槽机制将处理结果发送给主线程进行展示。 此外,为了提高接收速度,可以使用一些技巧。例如,使用一个循环缓冲区,将接收到的数据包放入缓冲区中,由处理线程从缓冲区中获取数据进行处理。又如,采用多路复用技术,监听多个端口上的数据包,以避免数据包丢失和延迟。 综上所述,基于UDP协议的多线程高速接收Qt工程,需要结合Qt的多线编程支持、信号和槽机制以及一些优化技巧来实现。相信在实现过程中遇到的问题都可以得到解决,从而实现高速接收并处理UDP数据包的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值