QT 中 QObject 作QT中类的最终父类,具有自定义信号与槽的能力,只要继承自这个类的类,也一样拥有自定义信号和槽的能力。QT 中定义信号与槽是十分有用的,那我们的线程类是不是也有这个能力呢?
查一下 QThread 的源码,我们发现 QThread 是继承自 QObject 的,他确实有自定义信号和槽的能力 !!!
class Q_CORE_EXPORT QThread : public QObject
{
。。。
};
那他有没有已经定义好的方便我们使用的信号呢?看一下源码就知道啦。
Q_SIGNALS:
void started(QPrivateSignal);
void finished(QPrivateSignal);
我们来看看 QThread 是不是真的支持这两个信号。
#include
#include
#include
#include
class ThreadTest : public QThread
{
public:
ThreadTest() {}
void run()
{
msleep(2000);
}
};
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr){}
public slots:
void getstarted()
{
qDebug()<<objectName()<<":"<<"getstarted()";
}
void getfinished()
{
qDebug()<<objectName()<<":"<<"getfinished()";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTest threada;
MyClass my;
my.setObjectName("my");
QObject::connect(&threada,SIGNAL(started()),&my,SLOT(getstarted()));
QObject::connect(&threada,SIGNAL(finished()),&my,SLOT(getfinished()));
threada.start();
return a.exec();
}
从运行结果上看确实收到了这两个信号呢。
在工程开发中,我们可以利用这两个信号来知道线程是什么时候开启和结束的。
QThread 已经定好了一些供我们使用的方便的信号,现在我们来试一试自定义信号和槽。
我们在刚才的代码上稍加修改
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr){}
public slots:
void getstarted()
{
qDebug()<<objectName()<<":"<<"getstarted()";
}
void getfinished()
{
qDebug()<<objectName()<<":"<<"getfinished()";
}
signals:
};
class ThreadTest : public QThread
{
Q_OBJECT
public:
ThreadTest()
{
connect(this,SIGNAL(counter(int)),this,SLOT(getCounter(int)));
connect(this,SIGNAL(counter_reset()),this,SLOT(on_counter_reset()));
}
void run()
{
int m_counter = 0;
for(int i = 0;i<10;i++)
{
if(0==i%5)
{
m_counter = 0;
emit counter_reset();
}
emit counter(m_counter);
m_counter = m_counter + 1;
msleep(200);
}
}
signals:
void counter(int n);
void counter_reset();
public slots:
void getCounter(int n)
{
qDebug()<< "getCounter : "<<n;
}
void on_counter_reset()
{
qDebug()<< "counter reset now !";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTest threada;
MyClass my;
my.setObjectName("my");
QObject::connect(&threada,SIGNAL(started()),&my,SLOT(getstarted()));
QObject::connect(&threada,SIGNAL(finished()),&my,SLOT(getfinished()));
threada.start();
return a.exec();
}
我们应该要知道
如果一个函数的函数体中没有访问临界资源的代码,那么这个函数可以被多个线程同时调用,不会产生任何副作用。因为线程调用函数的时候用的是自己的栈空间,既然线程的栈空间是独立的,那么谁调用函数就用谁的栈空间,互不干扰。
所以问题来了,槽函数本质也是个函数,那么槽函数是谁调用的啊???是主线程还是定义槽函数的线程还是其他线程???
一般来说,我们开启一个线程,那么这个线程应该会有自己的线程 id。为了知道当前线程的 id ,我们先看看QThread有没有提供方便我们使用的方法。
从 QThread 的类定义中我们可以找到这个玩意
static Qt::HANDLE currentThreadId() Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION;
那我们就试一试呗,看看是哪个线程调用槽函数。
#include
#include
#include
#include
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr){}
public slots:
void getstarted()
{
qDebug()<<objectName()<<":"<<"getstarted() , tid :"<<QThread::currentThreadId();
}
void getfinished()
{
qDebug()<<objectName()<<":"<<"getfinished(), tid :"<<QThread::currentThreadId();
}
signals:
};
class ThreadTest : public QThread
{
Q_OBJECT
public:
ThreadTest()
{
connect(this,SIGNAL(counter(int)),this,SLOT(getCounter(int)));
connect(this,SIGNAL(counter_reset()),this,SLOT(on_counter_reset()));
}
void run()
{
qDebug()<<"ThreadTest tid"<<currentThreadId();
int m_counter = 0;
for(int i = 0;i<10;i++)
{
if(0==i%5)
{
m_counter = 0;
emit counter_reset();
}
emit counter(m_counter);
m_counter = m_counter + 1;
msleep(200);
}
}
signals:
void counter(int n);
void counter_reset();
public slots:
void getCounter(int n)
{
qDebug()<< "tid:"<<currentThreadId()<< " , getCounter : "<<n;
}
void on_counter_reset()
{
qDebug()<< "tid:"<<currentThreadId()<< "counter reset now !";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug()<<"main tid:"<< QThread::currentThreadId();
ThreadTest threada;
MyClass my;
my.setObjectName("my");
QObject::connect(&threada,SIGNAL(started()),&my,SLOT(getstarted()));
QObject::connect(&threada,SIGNAL(finished()),&my,SLOT(getfinished()));
threada.start();
return a.exec();
}
运行结果
从运行结果看,我们的槽函数好像并不是在我们开启的线程中调用的呢,而是在主线程中。
可是我们槽函数在线程类中写的,却跑到主线程中调用了,大家不觉得奇怪吗。这样难道不会出现什么奇怪的 bug 吗?是 QT 不完善?还是别的原因?
QT 多线程信号与槽(二)