QObject::connect: Cannot queue arguments of type与线程数据共享

2014年4月20日 由 Creater 留言 »

Qt线程间共享数据主要有两种方式:
1.使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的;
2.使用singal/slot机制,把数据从一个线程传递到另外一个线程。

第一种办法在各个编程语言都使用普遍,而第二种方式倒是QT的特有方式,下面主要学习一下这种方式:

在线程之间传递signal与在一个线程内传递signal是不一样的。在一个线程内传递signal时,emit语句会直接调用所有连接的slot并等待到所有slot被处理完;在线程之间传递signal时,slot会被放到队列中(queue),而emit这个signal后会马上返回;默认情况,线程之间使用queue机制,而线程内使用direct机制,但在connect中可以改变这些默认的机制。
像QString等这些QT本身定义的类型,直接传送即可。但如果是自己定义的类型如果想使用signal/slot来传递的话,则没有这么简单。直接使用的话,会产生下面这种错误:
QObject::connect: Cannot queue arguments of type ‘TextAndNumber’ (Make sure ‘TextAndNumber’ is registed using qRegisterMetaType().)
原因:当一个signal被放到队列中(queued)时,它的参数(arguments)也会被一起一起放到队列中(queued起来),这就意味着参数在被传送到slot之前需要被拷贝、存储在队列中(queue)中;为了能够在队列中存储这些参数(argument),Qt需要去construct、destruct、copy这些对象,而为了让Qt知道怎样去作这些事情,参数的类型需要使用qRegisterMetaType来注册(如错误提示中的说明)

步骤:(以自定义TextAndNumber类型为例)

自定一种类型,在这个类型的顶部包含:#include
在类型定义完成后,加入声明:Q_DECLARE_METATYPE(TextAndNumber);
在main()函数中注册这种类型:qRegisterMetaType(“TextAndNumber”);
如果还希望使用这种类型的引用,可同样要注册:qRegisterMetaType(“TextAndNumber&”);

//TextAndNumber.h
#ifndef TEXTANDNUMBER_H
#define TEXTANDNUMBER_H
#include <QMetaType>
//必须包含QMetaType,否则会出现下面错误:
//error: expected constructor, destructor, or type conversion before ‘;’ token
#include <QString>
class TextAndNumber {
public:
    TextAndNumber();
    TextAndNumber(int, QString);
    int count();
    QString text();
private:
    int m_count;
    QString m_text;
};
Q_DECLARE_METATYPE(TextAndNumber);
#endif // TEXTANDNUMBER_H

//TextAndNumber.cpp
#include "TextAndNumber.h"
TextAndNumber::TextAndNumber() {
}
TextAndNumber::TextAndNumber(int count, QString text) {
    m_count = count;
    m_text = text;
}
int TextAndNumber::count() {
    return m_count;
}
QString TextAndNumber::text() {
    return m_text;
}

//TextDevice.h
#ifndef TEXTDEVICE_H
#define TEXTDEVICE_H
#include <QThread>
#include <QDebug>
#include <QString>
#include "TextAndNumber.h"
class TextDevice : public QThread {
    Q_OBJECT
public:
    TextDevice();
    void run();
    void stop();
public slots:
    void write(TextAndNumber& tran);
private:
    int m_count;
};

#endif // TEXTDEVICE_H

//TextDevice.cpp
#include "TextDevice.h"
TextDevice::TextDevice() : QThread() {
    m_count = 0;
}
void TextDevice::run() {
    exec();
}
void TextDevice::stop() {
    quit();
}
void TextDevice::write(TextAndNumber& tran) {
    qDebug() << QString("Call %1 (%3): %2").arg(m_count++).arg(tran.text()).arg(tran.count());
}

//TextThread.h
#ifndef TEXTTHREAD_H
#define TEXTTHREAD_H
#include <QThread>
#include <QString>
#include "TextAndNumber.h"
class TextThread : public QThread {
    Q_OBJECT
public:
    TextThread(const QString& text);
    void run();
    void stop();
signals:
    void writeText(TextAndNumber& tran);
private:
    QString m_text;
    int m_count;
    bool m_stop;
};

#endif // TEXTTHREAD_H

//TextThread.cpp
#include "TextThread.h"
TextThread::TextThread(const QString& text) : QThread() {
    m_text = text;
    m_stop = false;
    m_count = 0;
}
void TextThread::run() {
    while(!m_stop) {
        TextAndNumber tn(m_count++, m_text);
        emit writeText(tn);
        sleep(1);
    }
}
void TextThread::stop() {
    m_stop = true;
}

//main.cpp
#include <QApplication>
#include <QMessageBox>
#include "TextThread.h"
#include "TextDevice.h"
#include "TextAndNumber.h"
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    qRegisterMetaType<TextAndNumber>("TextAndNumber");
    qRegisterMetaType<TextAndNumber>("TextAndNumber&");
    TextDevice device;
    TextThread foo("foo"), bar("bar");
    QObject::connect(&foo, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));
    QObject::connect(&bar, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));
    device.start();
    foo.start();
    bar.start();
    QMessageBox::information(0, "Threading", "Click me to close");

    foo.stop();
    bar.stop();
    device.stop();
    foo.wait();
    bar.wait();
    device.wait();
    qDebug() << "Application end.";
    return 0;
}
广告位

评论已关闭.