【Qt线程-1】this,volatile,exec(),moveToThread()

背景:

个人学习多线程控制,写了一些博文用于记录:

【Qt线程-1】this,volatile,exec(),moveToThread()

【Qt线程-2】事件循环(QCoreApplication::processEvents,exec)的应用

【Qt线程-3】使用事件循环,信号,stop变量,sleep阻塞,QWaitCondition+QMutex条件变量,退出子线程工作

【Qt线程-4】事件循环嵌套,BlockingQueuedConnection与QWaitCondition比较

【Qt线程-5】生产者&消费者模型应用(多态,子线程控制,协同,事件循环)

【Qt线程-6】获取当前线程id,thread()和currentThreadId(),不是想当然那样,不使用信号槽可能看不出区别

线程对象和线程执行

两者不是一回事。很多人说法不同,但都是一个意思。为了叙述方便,姑且称之为子线程,因为所有多线程处理,都是相对于主线程而言的。

比如在主线程中新开一个线程,需要类似这样的操作:

QThread *thd = new Qthread;

当然有时候是从Thread派生一个自定义的线程类。就如在主线程里创建一个对象没两样。

所以thd仅仅是主线程中的一个对象,thd这个对象,是用来管理子线程的。亦即,谁要操作这个子线程,必定要通过thd来操作,比如:

thd.start();
thd.stop();
thd.quit();
thd.wait();
thd.deleteLater();
...

就如操作某个控件一样来操作子线程。有点类似于windows的句柄。

因此,在定义thd的类代码中,所有成员变量和函数,都是在thd这个对象当中。如果没有run函数的参与,都会运行于主线程中。例如写个槽函数之类,相当于还是运行于主线程。

千万不能认为发给thd一个信号,只要它响应就应该是运行于子线程,这是不对的。

善用QDebug

在恰当位置输出信息,比如:

qDebug() << "frmMain::f_ClientConnect():The main thread is:"

                << this->thread()->currentThreadId();

qDebug() << "ThreadConn::f_ConnectCome() runs on thread:"

                << this->currentThreadId();

qDebug() << "ThreadConn::f_ConnectCome():tcpSocket runs on thread:"

                << m_tcpSocket->thread()->currentThreadId();

在调试时可以随时看到指定位置跑在哪个线程。

run()函数

run函数相当于真正线程过程的入口。所以通过run启动执行的代码,才是跑在子线程中。run函数调用的所有代码执行完,线程就完了。而这些通过run调用的代码,是可以使用thd类中定义的变量的。

this指针

在thd子线程的类代码中,this指针指向的是thd对象,它的parent是主线程,所以this其实是工作于主线程的。这点必须要理解,不是说在线程代码中使用this,就表示子线程了。

举个例子,如果在子线程中新建一个对象,像这样:

QObject *obj = new QObject(this);

开始我这样写也觉得没什么不妥,后来发现会报错的。思考之后才知道不能这样写,因为这相当于在子线程里创建一个属于主线程的对象,于是会有类似如下这样的提示:

Cannot create children for a parent that is in a different thread.

所以如果真的需要在子线程创建对象,去掉那个this就好了,如:

QObject *obj = new QObject();

exec和moveToThread

run函数中写个exec,是系统的事件循环,类似于死循环,但它占用资源很小。它可以让run保持运行,不是阻塞,它可以随时接收系统事件,但这个事件不是随便发个信号它就能响应的。

如果一个线程已经在运行,某个时刻希望有代码加入到exec的队列中,就写个子类,定义好响应信号的槽函数。实例化对象,比如叫obj,使用obj.moveToThread(thd)将它移动到子线程thd。然后给obj发信号让它响应就可以了。此时,obj当中的槽响应代码是可以加入exec的响应队列,它是在子线程中执行的。

Qt官方帮助这样写:

void QObject::moveToThread(QThread *targetThread)

Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.

……

我英文就不好,但是从学习linux和qt以来,发现有些帮助写的其实挺好理解的,主要是这些词都认识,不认识就百度翻译,见多就记住了。

volatile

这是个很好的关键字,看操作系统教科书上的代码,总有while (true){}这种死循环,然后通过判断某个变量决定是否跳出。但是单线程里直接这样写就真的死循环了。好像那个作为判断条件的变量不是那么理想化的。

线程代码里定义某个变量当做标记时,加上volatile关键字,例如这样:

volatile bool b = false;

这样它就听话了。至于为什么,直接网上搜索关键字volatile,各种好文章说的很到位。有位朋友这样写:

“线程之间的可见性,一个线程修改的状态对另一个线程可见,也就是一个线程修改的结果,另一个线程马上就能看到。”

已经很好理解了。这属于内存问题,如果不想深入理解,会用即可。

其实我试过,不用volatile貌似也能用。但有些时候又是必要的,所以是否需要使用,自己斟酌。

参考:C/C++ 中 volatile 关键字详解icon-default.png?t=N7T8https://www.runoob.com/w3cnote/c-volatile-keyword.html

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值