Qt多线程通信 (附demo)

本人复数代码源码下载地址:https://download.csdn.net/download/flowerspring/11022121

简述:

1> Qt线程间共享数据主要有两种方式:

1)使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。

2)使用singal/slot机制,把数据从一个线程传递到另外一个线程。

第一种方法在各个编程语言都普遍使用,而第二种方法是QT的特有的,本文主要介绍第二种。

2 > 槽参数类型

1) 在线程间使用信号槽进行通信时,槽参数必须使用元数据类型的参数;

2) Qt内生的元数据类型,如int double QString等;

3) 如果要用自定义的数据类型,需要在connect之前将其注册(qRegisterMetaType)为元数据类型。

4) 线程间用“信号与槽”传递引用参数的话,要加const,因为const文字常量存在常量区中,生命周期与程序一样的长。这样可以避免slot调用的时候参数的运行期已过而使引用无效。

connect(m_thread, SIGNAL(MsgSignal(const QString&)),
              this, SLOT(OnMsgSignal(const QString&)));

3 > Q_DECLARE_METATYPE与qRegisterMetaType 
Q_DECLARE_METATYPE

如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。

该类型必须有公有的 构造、析构、复制构造函数。

qRegisterMetaType 

必须使用该函数的两种情况:

如果非QMetaType内置类型要在Qt的属性系统中使用。

如果非QMetaType内置类型要在queued 信号与槽中使用。

两者的关系:

Q_DECLARE_METATYPE展开后是一个特化后的类QMetaTypeId

qRegisterMetaType将某类型注册到MetaType系统中。

QMetaTypeId的类中成员包含对qRegisterMetaType的调用。

1、传递int参数(主线程与子线程)

 

testthread.h 文件

 

 
  1. #ifndef TESTTHREAD_H

  2. #define TESTTHREAD_H

  3.  
  4. #include

  5.  
  6. #include "msg.h"

  7.  
  8. class TestThread : public QThread

  9. {

  10. Q_OBJECT

  11. public:

  12. explicit TestThread(QObject *parent = 0);

  13.  
  14. protected:

  15. void run();

  16.  
  17. signals:

  18. void TestSignal(int);

  19.  
  20. private:

  21. Msg msg;

  22. };

  23.  
  24. #endif // TESTTHREAD_H

 

testthread.cpp文件

 

 
  1. #include "testthread.h"

  2.  
  3. TestThread::TestThread(QObject *parent) :

  4. QThread(parent)

  5. {

  6. }

  7.  
  8. void TestThread::run()

  9. {

  10. //触发信号

  11. emit TestSignal(123);

  12. }


自定义的类继承了QThread类,重写run函数,然后触发TestSignal信号。

mainwindow.h

 
  1. #ifndef MAINWINDOW_H

  2. #define MAINWINDOW_H

  3.  
  4. #include

  5.  
  6. #include "testthread.h"

  7.  
  8. namespace Ui {

  9. class MainWindow;

  10. }

  11.  
  12. class MainWindow : public QMainWindow

  13. {

  14. Q_OBJECT

  15.  
  16. public:

  17. explicit MainWindow(QWidget *parent = 0);

  18. ~MainWindow();

  19.  
  20. private slots:

  21. void DisplayMsg(int);

  22.  
  23. private:

  24. Ui::MainWindow *ui;

  25. TestThread *t;

  26. };

  27.  
  28. #endif // MAINWINDOW_H

 

mainwindow.cpp

 

 
  1. #include "mainwindow.h"

  2. #include "ui_mainwindow.h"

  3.  
  4. MainWindow::MainWindow(QWidget *parent) :

  5. QMainWindow(parent),

  6. ui(new Ui::MainWindow)

  7. {

  8. ui->setupUi(this);

  9.  
  10. //进行connect前必须实例化

  11. t = new TestThread();

  12.  
  13. connect(t, SIGNAL(TestSignal(int)), this, SLOT(DisplayMsg(int)));

  14.  
  15. //执行子线程

  16. t->start();

  17. }

  18.  
  19. void MainWindow::DisplayMsg(int a)

  20. {

  21. ui->textBrowser->append(QString::number(a));

  22. }

  23.  
  24. MainWindow::~MainWindow()

  25. {

  26. delete ui;

  27. }


Mainwindow里面连接信号槽,并且将收到的int参数显示在 界面上。
运行效果:

 

Qt多线程通信 (附demo)_第1张图片

 

2、传递自定义参数(主线程与子线程)

 

对以上程序进行简单的修改,使它传递自定义消息。

testthread.h 文件

 

 
  1. #ifndef TESTTHREAD_H

  2. #define TESTTHREAD_H

  3.  
  4. #include

  5.  
  6. #include "msg.h"

  7.  
  8. class TestThread : public QThread

  9. {

  10. Q_OBJECT

  11. public:

  12. explicit TestThread(QObject *parent = 0);

  13. Msg msg;

  14.  
  15. protected:

  16. void run();

  17.  
  18. signals:

  19. void TestSignal(Msg); //自定义消息Msg!!!

  20. };

  21.  
  22. #endif // TESTTHREAD_H


testthread.cpp文件

 

 
  1. #include "testthread.h"

  2.  
  3. TestThread::TestThread(QObject *parent) :

  4. QThread(parent)

  5. {

  6. }

  7.  
  8. void TestThread::run()

  9. {

  10. msg.int_info = 999;

  11. msg.str_info = "Hello Main Thread!";

  12. //触发信号

  13. emit TestSignal(msg);

  14. }


mainwindow.h

 

 
  1. #ifndef MAINWINDOW_H

  2. #define MAINWINDOW_H

  3.  
  4. #include

  5.  
  6. #include "testthread.h"

  7. #include "msg.h"

  8.  
  9. namespace Ui {

  10. class MainWindow;

  11. }

  12.  
  13. class MainWindow : public QMainWindow

  14. {

  15. Q_OBJECT

  16.  
  17. public:

  18. explicit MainWindow(QWidget *parent = 0);

  19. ~MainWindow();

  20.  
  21. private slots:

  22. void DisplayMsg(Msg); //Msg!!!

  23.  
  24. private:

  25. Ui::MainWindow *ui;

  26. TestThread *t;

  27. };

  28.  
  29. #endif // MAINWINDOW_H

 

mainwindow.cpp

 

 
  1. #include "mainwindow.h"

  2. #include "ui_mainwindow.h"

  3.  
  4. MainWindow::MainWindow(QWidget *parent) :

  5. QMainWindow(parent),

  6. ui(new Ui::MainWindow)

  7. {

  8. ui->setupUi(this);

  9.  
  10. //进行connect前必须实例化

  11. t = new TestThread();

  12.  
  13. //Msg!!!

  14. connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));

  15.  
  16. //执行子线程

  17. t->start();

  18. }

  19.  
  20. void MainWindow::DisplayMsg(Msg msg)

  21. {

  22. ui->textBrowser->append(QString::number(msg.int_info));

  23. ui->textBrowser->append(msg.str_info);

  24. }

  25.  
  26. MainWindow::~MainWindow()

  27. {

  28. delete ui;

  29. }


此时再进行编译,编译通过,但Qt Creator会有提示:

 

 

 
  1. QObject::connect: Cannot queue arguments of type 'Msg'

  2. (Make sure 'Msg' is registered using qRegisterMetaType().)


并且运行程序时会发现,信号发送了,槽函数始终不调用。

 

如果将槽参数注册为元数据类型,即mainwindow.cpp文件改动一下:

 

 
  1. ui->setupUi(this);

  2.  
  3. qRegisterMetaType("Msg");


此时便可正常运行:

 

Qt多线程通信 (附demo)_第2张图片

 

 

3、传递自定义参数(子线程与子线程)

 

 

原理同上,然后把connect函数中的第三参数this(主线程)改成要监听的另一个线程对象就好了(QT多么健壮、友好、强大)。

 

connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));  

前提是全部的线程都要在主线程里面实例化(new)。

 

 

4、传递自定义结构体参数(子线程与子线程)

 

实现子线程与GUI子线程的参数进行传递。

线程

头文件 ABFThread.h

 

 
  1. public:

  2.  
  3. struct G_ABFTableSrcUnit

  4. {

  5. int a;

  6. int b;

  7. int c;

  8. float d;

  9. float e;

  10. unsigned int f;

  11. float Gg;

  12.  
  13. QString waveformTypel;

  14. };

  15.  
  16. public slots:

  17.  
  18. void parameterPassing(abfThread::G_ABFTableSrcUnit); //线程自己调用自己的结构体。。。必须这么写不然主线程会报错的 错误是参数内容不一样


ABFThread.cpp

 

 

 
  1. void abfThread::parameterPassing(abfThread::G_ABFTableSrcUnit)

  2. {

  3.  
  4. }


GUI线程 

radarControl.h

 

 
  1. #include "abfThread"

  2.  
  3. private:

  4. Ui::radarControl *ui;

  5.  
  6. abfThread::G_ABFTableSrcUnit mst_abfSrcUnit;

  7.  
  8.  
  9. signals:

  10. void sendString(abfThread::G_ABFTableSrcUnit);

radarControl.cpp
按下按钮就发射信号

 

 
  1. void radarControl::on_pushButton_clicked()

  2. {

  3. emit sendString(mst_abfSrcUnit);

  4. }

mainwindow.h
 
  1. #include "abfThread.h"

  2. #include "radarControl.h"

mainwindow.cpp
 
  1. radarInterface = new radarControl();

  2. m_ABFThread = new QThread();

  3. m_ABF = new abfThread();

  4. m_ABF->moveToThread(m_ABFThread);

  5. m_ABFThread->start();

  6.  
  7. qRegisterMetaType("abfThread::G_ABFTableSrcUnit");

  8. connect(radarInterface,SIGNAL(sendString(abfThread::G_ABFTableSrcUnit)),m_ABF,SLOT(parameterPassing(abfThread::G_ABFTableSrcUnit)));

  9. //除了注册结构体外 还要保证传递的参数写法要一样 这就是为什么 前面线程自己定义的结构体自己调用自己的原因了

 

 

小结:

 

1 > Qt的信号槽函数只默认支持Qt的类型和C++提供的内建的基本类型,比如int double float等,根本不支持C++的std::string std::vector 自定义的struct类型。所以需要用Qt提供的Q_DECLARE_METATYPE和qRegisterMetaType来声明和注册自定义的类型和C++的其他类型。
2 > 多线程间的信号槽传递,在connect的时候需要以Qt::QueuedConnection的方式,不然以Qt::DirectConnection的方式接收者UI线程会很长时间收不到后台线程发出的信号,或者信号直接丢失都是有可能的

你可能感兴趣的:(Qt,c++)