多线程(Qt)里控制资源的顺序访问
Qt应用程序如果没有特别引用多线程的类或方法,是仅有一个GUI线程(起初就是一个图形库)的,线程的ID可以通过QThread::currentThreadId()进行验证;
多数情况下,QT应用已经是一个前后端结合体,不仅用于GUI前端,而且也会有一些后台任务,因此免不了要引入非GUI线程(或称为工作线程),在工作线程中从事一些耗时或频繁的非GUI活动;
那么怎么引入多线程?多线程里怎么保证顺序访问某资源呢?
在最初的QT版本里,引入另一个线程,需要通过QThread。它要么继承QThread,并重新实现run()方法;要么QObject对象通过moveToThread()方法移入到另一个线程(QThread类)中;后来,则出现了继承于QRunnalbe的对象通过线程池启动的方式,以及使用QtConcurrent ::run()运行的方式;
具体的可参考下面的网页:
https://blog.csdn.net/Spwper/article/details/80292004?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
(需要说明这个QtConcurrent::run(),不但支持调用外部函数,还支持调用类成员函数,还支持Lambda函数;与其相关的QFuture也是借用Java的分布式框架常用方式,相当先进;Qt旧版本不可用)
现着重说明最简单的继承QThread的方式(因为后面要用到),如下所示:

注意:通过QThread::start()方法启动的,就是重载的QThread::run()函数,这个函数闭包里的才是工作线程的代码,其他的仍是GUI线程,而且run()内一般会有一个while(1) {...} 循环,除非这个多出来的线程只要求干活一次。
在多线程里,对某个共同的资源的访问是竞争的,如下所示:

启用WorkThreadA和WorkThreadB各一个对象,则打印可能是“A+ B- B- A+ A+ ....”,也可能是"A+ A+ B- B- B-....", 某一时刻src的值每次运行都是不确定的。
特别是在生产者-消费者模型里,WorkThreadA对象作生产者,WorkThreadB对象作消费者,这样不加控制的方式显然是不可行(生成者-消费者模型里生产者生成的资源刚好被消费者消费掉)。
同一线程里资源是顺序访问的,但是多线程里顺序则是不可控的,可能资源刚要改动,就被另一线程给改了;或者当前线程改了好几次,另一线程才改一次的情况;要在多线程里完全控制这个资源的顺序访问,需要借助两个东西:互斥锁和信号;
/* ~~~~~~ 新代码是这样的 ~~~~~~~~~~~~~~ */

说明:
1、这里有一把锁g_mutex和一信号g_cond, 两个线程共用,相关部分打了注释*或**;
2、锁的机制是:一方拿到锁,锁住资源;另一方没拿到锁的,就只能等,一直等到锁资源被释放了,才能拿到锁往下执行;
3、信号的机制是:开始等待时就立马将锁资源释放,然后在系统级别阻塞住,不让往下走,一直在等唤醒信号,一旦等到信号被唤醒,则立马拿到锁解除阻塞,(对方因为没锁资源反而被套住了);
4、整个流程是这样的: 线程A先启动,拿到锁(可以做src++),此时线程B是拿不到锁的,更别提到去操作src了;然后线程A一路到信号wait那里阻塞住,此时锁资源释放了;线程B可以运作了,开始减少资源并打印,然后到了唤醒信号(wakeAll)那里并放锁;线程A被唤醒了,此时线程B挂机了,然后线程A又可以继续锁,并做增加资源和打印,此时锁锁住所以线程B技术不挂机也无法运行了。现在打印总是"A+ B- A+ B- A+ B- ..."
5、因为锁的lock()与unlock()总是同步出现的,为防止漏掉解锁造成卡死的情况,所以有QMutexLocker的用法,用了它就可以在栈生命期结束时自动解锁;然后代码变成这样:

浙公网安备 33010602011771号