几篇比较好的文章
介绍moveToThread方式创建线程的
这篇文章写的是真好,也有对线程的介绍
第二篇文章中有这样一段话,我遇到过下面的问题,但也没太理解透这里是什么意思。后来考了这篇文章:对于线程上槽函数不执行的问题说的很好,我自己也执行了一下他的代码,进行了一些改动。
在QThread的使用方式上,就建议使用这种方式,而不是传统的通过继承QThread,并重写run函数来实现线程。
因为通过继承QThread方式创建的线程不会开启事件循环,会导致给在这个线程上创建的QObject对象发信号时,槽函数得不到执行的问题。
至此,我们明白了上面那段引用的话的含义,这和线程亲和性(affinity)有关系。因为重写了run函数属于一个单独的线程,在这个线程中没有开启事件循环,线程的亲和性导致无法发送信号或执行槽函数。
所以上述例子中的代码,不能触发对应槽函数,有两种原因,分别是无法发送信号和无法执行槽函数。
void CTestThread::run()
{
while (true)
{
// qDebug() << __FUNCTION__ << QThread::currentThread();
QThread::sleep(2);
if (ptr_owned_ == nullptr)
{
// 子线程中创建对象,所以ptr_owned_ 所属对象为子线程
ptr_owned_ = new CSignalSlot;
//无法执行OnSlot函数 : 因为ptr_owned_在run函数所在线程中,所以无法触发槽函数
//SignalTestSlot信号有主线程发出
connect(ptr_test_, &MainWindow::SignalTestSlot, ptr_owned_, &CSignalSlot::OnSlot, Qt::QueuedConnection);
//这种写法就可以执行,
QTimer::singleShot(1000, this, [this]() {
qDebug() <<"******"<< __FUNCTION__ << QThread::currentThread();
});
//这种写法也可以执行,他和上面的lambda表达式一样,是传递的函数指针
QTimer::singleShot(1000, this,&CTestThread::timeOutTest);
//无法执行timeOutTest函数 : 原因和下面的QTimer 同理,但是这种写法就不能触发,这个得好好探究一下了
QTimer::singleShot(1000, this,SLOT(timeOutTest()));
//无法执行timeOutTest函数 : 因为t 在run函数所在的线程中,无法发送信号
QTimer *t = new QTimer;
connect(t, SIGNAL(timeout()), this, SLOT(timeOutTest()));
//就算换成下面的写法也不会触发,道理是一样的
// connect(t, &QTimer::timeout, this, &CTestThread::timeOutTest);
t->start(1000);
}
}
}
网上有很多人把上述代码做如下修改就可以使槽函数执行了,但这会导致两个线程同时操作一个对象,如果这个对象不是线程安全的就可能导致程序崩溃,而且错误很难找到。最好还是不要这样,老老实实用moveToThread,或者使用其他的方式。
connect(ptr_test_, &MainWindow::SignalTestSlot, ptr_owned_, &CSignalSlot::OnSlot, Qt::QueuedConnection);
改为
connect(ptr_test_, &MainWindow::SignalTestSlot, ptr_owned_, &CSignalSlot::OnSlot, Qt::DirectConnection);
上面我们说过这一切发生的原因是因为重写了run函数,导致无法进行事件循环,所以原来run函数都做了什么呢?
看一下QThread::run()的代码,很简单,就一句话,原来只是通过exec开启了事件循环
QThread::run()
{
(void) exec();
}
那我们当然也可以在重写的run函数中调用exec开启事件循环啊。
所以可以在上面代码的最后调用exec,这样上面的信号和槽的触发就都正常了,但是因为exec函数会阻塞所在的函数,所以这里的while循环只能执行一次。
void CTestThread::run()
{
while (true)
{
qDebug()<<__FUNCTION__;
QThread::sleep(2);
if (ptr_owned_ == nullptr)
{
ptr_owned_ = new CSignalSlot;
connect(ptr_test_, &MainWindow::SignalTestSlot, ptr_owned_, &CSignalSlot::OnSlot, Qt::QueuedConnection);
QTimer::singleShot(1000, this, [this]() {
qDebug() << "this is in CTestThread --- QTimer::singleShot";
});
QTimer::singleShot(1000, this,SLOT(timeOutTest()));
QTimer *t = new QTimer;
connect(t, SIGNAL(timeout()), this, SLOT(timeOutTest()));
t->start(100);
//因为exec会阻塞函数运行,所以要在最后加上。
(void)exec();
}
}
}
QTimer
参考https://blog.csdn.net/liang19890820/article/details/51789796
Qt使用定时器的线程关联,以确定哪个线程会发出timeout()信号。正因为如此,你必须在它的线程中启动和停止定时器,
不可能从另一个线程启动定时器。
也就是说timer的start等操作要和timer在同一个线程上,不然会报下面的错误。
QObject::startTimer: Timers can only be used with threads started with QThread