Qt中的线程

几篇比较好的文章
介绍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
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值