搜档网
当前位置:搜档网 › Qt多线程编程中的对象线程与函数执行线程

Qt多线程编程中的对象线程与函数执行线程

Qt多线程编程中的对象线程与函数执行线程
Qt多线程编程中的对象线程与函数执行线程

Qt多线程编程中的对象线程与函数执行线程

分类:QT编程多线程2012-03-14 17:46 7604人阅读评论(6) 收藏举报qt多线程编程threadclasssignal

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

[cpp]view plaincopy

1.// define Object class<

/span>

[cpp]view plaincopy

1.class SomeObject : public Q

Object

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.};

13.

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

[cpp]view plaincopy

1.class SubThread : public QT

hread

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函数。定义如下:

[cpp]view plaincopy

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个不同的线程并运行,查看运行结果。

[cpp]view plaincopy

1.int main(int argc, char *ar

gv[])

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的实现:

[cpp]view plaincopy

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,就不再是主线程了。

[cpp]view plaincopy

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对象的线程执行。如

[cpp]view plaincopy

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.}

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

[plain]view plaincopy

1."SubThread1::obj's thread i

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

/span>

相关主题