近来用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信号。定义如下:

  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;"

Qt多线程编程中的对象线程与函数执行线程的更多相关文章

  1. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  2. Java多线程编程中Future模式的详解<转>

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  3. 关于python多线程编程中join()和setDaemon()的一点儿探究

    关于python多线程编程中join()和setDaemon()的用法,这两天我看网上的资料看得头晕脑涨也没看懂,干脆就做一个实验来看看吧. 首先是编写实验的基础代码,创建一个名为MyThread的  ...

  4. C++ 关于MFC多线程编程中的一些注意事项 及自定义消息的处理

    在多线程编程中,最简单的方法,无非就是利用 AfxBeginThread  来创建一个工作线程,看一下这个函数的说明: CWinThread* AFXAPI AfxBeginThread( AFX_T ...

  5. Java多线程编程(2)--多线程编程中的挑战

    一.串行.并发和并行   为了更清楚地解释这三个概念,我们来举一个例子.假设我们有A.B.C三项工作要做,那么我们有以下三种方式来完成这些工作:   第一种方式,先开始做工作A,完成之后再开始做工作B ...

  6. C#多线程编程中的锁系统

    C#多线程编程中的锁系统(二) 上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了. 这一次我们说说升级锁和原子操作. 目录 1:volatile 2:  Inter ...

  7. 【C/C++开发】多线程编程中的join函数

    多线程编程中的join函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # coding: utf-8 # 测试多线程中join的 ...

  8. [Vue]vue中各选项及钩子函数执行顺序

    在vue中,实例选项和钩子函数和{{}}表达式都是不需要手动调用就可以直接执行的. 一.生命周期图示 二.vue中各选项及钩子函数执行顺序 1.在页面首次加载执行顺序有如下: beforeCreate ...

  9. Qt多线程编程总结(一)(所有GUI对象都是线程不安全的)

    Qt对线程提供了支持,基本形式有独立于平台的线程类.线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法. 这个文档是提供给那些对多线程编程有丰富的知识和经验的听众的.推荐阅 ...

随机推荐

  1. RHEL/Centos下Sendmail服务器搭建

    目的 Linux下配置Sendmail服务器,并通过客户端验证. 环境 Cento6 局域网(可访问互联网) 内容 配置Sendmail服务器,使得客户端能够通过foxmail或者outlook ex ...

  2. CentOS7无法使用tab补全功能??

    Centos7在使用最小化安装的时候,没有安装自动补全的包,需要自己手动安装. yum -y install bash-completion 或者你可以安装一些初始化的包组 yum -y groupi ...

  3. python3 执行AES加密方法

    cmd执行命令:pip install pycryptodome # -*- coding: utf-8 -*- # __author__ = 'Carry' import base64 from C ...

  4. .net core2.0入门使用EF

    使用Nuget导入所需要的EF 核心包以及对应数据库的驱动包,我用的是sqlserver(.net 支持的所有数据库) Install-Package Microsoft.EntityFramewor ...

  5. SPOJ_VLATTICE

    题目是给你一个空间,和一个点(n,n,n),求从原点出发能够直接接触多少个点(不经过任何一个点)? 典型的mobius反演即可. 首先,ans=3,因为(1,0,0),(0,1,0),(0,0,1)这 ...

  6. SHA1WithRSA签名 规范化标准签名

    #region CerRsaSignature 根据私钥签名 /// <summary> /// 根据私钥串签名 /// </summary> /// <param na ...

  7. ActiveMQ反序列化漏洞(CVE-2015-5254)复现

      0x00 漏洞前言 Apache ActiveMQ是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务,集群,Spring Framework等.Apache ...

  8. 《Linux内核设计与实现》学习总结 Chap3

    第三章 进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限 ...

  9. 20135319zl字符集报告

    字符集实验 ASCII 首先,查找ZHULI五个字符对应的ASCII码,5a 48 55 4c 49. 然后,用vim打开一个空文档,按下":",输入%!xxd 然后,输入 000 ...

  10. java关于类加载的面试题

    ---面试题 class SingleTon { private static SingleTon singleTon = new SingleTon(); public static int cou ...