qt定时信号量_Qt基础开发之Qt多线程类QThread与Qt定时器类QTimer的详细方法与实例..._网易王三三的博客-程序员秘密

技术标签: qt定时信号量  

Qt多线程

我们之前的程序都是单线程运行,接下来我们开始引入多线程。就相当于以前的一个人在工作,现在多个人一起工作。

Qt中非常有必要使用多线程,这是因为,Qt应用是事件驱动型的,一旦某个事件处理函数处理时间过久,就会造成其它的事件得不到及时处理。

Qt中使用QThread来管理线程,一个QThread对象,就是一个线程。QThread对象也有消息循序exec()函数,用来处理自己这个线程的事件。

Qt实现多线程有两种方式

​1、Qt第一种创建线程方式

首先要继承QThread

重写虚函数QThread::run

[virtual protected] void QThread::run()

/*

* 基类QThread的run函数只是简单启动exec()消息循环

*/

例如:

#include

#include

#include

class MyThread : public QThread

{

public:

void run()

{

qDebug() << "QThread begin" << endl;

qDebug() << "child thread" << QThread::currentThreadId() << endl;

QThread::sleep(5);

qDebug() << "QThread end" << endl;

exec();

}

};

int main(int argc, char** argv)

{

QApplication app(argc, argv);

MyThread thread;

thread.start();

qDebug() << "main thread" << QThread::currentThreadId() << endl;

QThread::sleep(5);

qDebug() << "main thread" << QThread::currentThreadId() << endl;

thread.quit();

qDebug() << "main thread thread.quit()" << endl;

tread.wait();

qDebug() << "main thread thread.wait()" << endl;

return app.exec();

}

使用QThread的quit可以退出线程的消息循环,有时候不是马上退出,需要等到cpu的控制权交还给线程的exec()。

一般在子线程退出的时候需要主线程去回收资源,可以调用QThread的wait,等待子线程的退出,然后回收资源.

2、Qt第二种创建线程方式

继承 QObject

实例化一个QThread对象

实现槽函数.

QObject子类对象通过moveToThread将自己放到线程QThread对象中.

调用QThread对象的start函数启动线程

必须通过发射信号来让槽函数在线程中执行,发射的信号存放在线程exec()消息队列中。

例如:

mywork.h

#ifndef MYWORK_H

#define MYWORK_H

#include

#include

class MyWork : public QObject

{

Q_OBJECT

public slots:

void workSlot()

{

qDebug() << "QThread begin" << endl;

qDebug() << "child thread" << QThread::currentThreadId() << endl;

QThread::sleep(5);

qDebug() << "QThread end" << endl;

}

};

#endif // MYWORK_H

widget.cpp

#include

#include

#include

#include "mywork.h"

int main(int argc, char** argv)

{

qDebug() << "main thread" << QThread::currentThreadId() << endl;

QApplication app(argc, argv);

QThread thread;

MyWork work;

work.moveToThread(&thread);

QObject::connect(&thread, SIGNAL(started()), &work, SLOT(workSlot()));

thread.start();

QThread::sleep(6);

qDebug() << "thread is runing" << thread.isRunning() << endl;

thread.quit(); //调用quit让线程退出消息循环,否则线程一直在exec循环中

thread.wait(); //调用完quit后紧接着要调用wait来回收线程资源

qDebug() << "thread is runing" << thread.isRunning() << endl;

return app.exec();

}

需要特别注意的是:

线槽函数已经执行完进入线程exec()中,可以通过发射信号重新让槽函数在线程中执行。也可以通过quit()退出线程exec()。

QObject派生类对象,将要调用moveToThread,不能指定一个主线程父对象托管内存。

QWidget的对象及派生类对象都只能在GUI主线程运行,不能使用moveToThread移到子线程中,即使没有指定父对象。

多线程对象内存释放

既然QObject对象无法托管内存对象,那么到底是先释放线程对象,还是先释放这个QObject对象?

先把QObject在线程循环中释放(使用QObject::deleteLater函数),然后QThread::quit,然后QThread::wait。

例如:

​mywork.h

#ifndef MYWORK_H

#define MYWORK_H

#include

#include

class MyWork : public QObject

{

Q_OBJECT

public:

~MyWork() { qDebug() << __FUNCTION__ << endl; }

public slots:

void workSlot()

{

while(1)

{

qDebug() << "Work begin" << endl;

QThread::sleep(5);

qDebug() << "work end" << endl;

}

}

void otherWorkSlot()

{

qDebug() << "otherWork begin" << endl;

QThread::sleep(5);

qDebug() << "otherWork end" << endl;

}

};

#endif // MYWORK_H

widget.h

#ifndef WIDGET_H

#define WIDGET_H

#include

#include "mywork.h"

class Widget : public QWidget

{

Q_OBJECT

public:

Widget(QWidget *parent = 0);

~Widget();

private:

QThread *_thread;

MyWork * _myWork;

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"

#include

#include "mywork.h"

#include

#include

Widget::Widget(QWidget *parent)

: QWidget(parent)

{

_myWork = new MyWork;

_thread = new QThread(this);

_myWork->moveToThread(_thread);

_thread->start();

QPushButton *pb0 = new QPushButton("work", this);

QPushButton *pb1 = new QPushButton("pb", this);

QHBoxLayout *hBox = new QHBoxLayout(this);

hBox->addWidget(pb0);

hBox->addWidget(pb1);

this->setLayout(hBox);

/*发射信号给在另外一个线程的对象的队列中*/

connect(pb0, SIGNAL(clicked()), _myWork, SLOT(workSlot()));

connect(pb1, SIGNAL(clicked()), _myWork, SLOT(otherWorkSlot()));

/*推荐用法释放内存*/

//connect(_thread, SIGNAL(finished()), _myWork, SLOT(deleteLater()));

}

Widget::~Widget()

{

_myWork->deleteLater(); //一定要在QThread线程退出之前

_thread->quit();

_thread->wait();

}

3、Qt线程的同步

​多线程在访问同时一个资源,(例如:多个线程可操作的变量、函数等),到底谁来使用这个资源是一个问题,就像一大群人去抢同一块蛋糕,可能其中一个人抢到,更有可能蛋糕被抢个稀烂。在多线程中,这个叫作竞争冒险。那么我们需要定一个规则来约束每一个人,比如:每个人排队来领蛋糕,这个在多线程中叫作同步方法。

​需要注意的是,同步不是同时,而是有序进行。

3.1、互斥锁

​Qt中的互斥锁是QMutex,不继承任何Qt基类,使用QMutex来锁共享资源,哪个线程抢到钥匙,哪个线程就有这个资源的使用权,其它线程等待这个线程使用完资源并归还钥匙,然后它们再去抢钥匙。

​例如:

QMutex mutex; //这对象一般定义在多个线程能访问的地方

mutex.lock(); //多个线程调用这个函数去获取锁,没有获取到的线程,将阻塞等待在这个函数上。

mutex.unlock(); //释放锁

QMutex::lock函数会让线程等待获取锁,如果不想等待,可以使用一下函数替换:

bool QMutex::tryLock(int timeout = 0)

/*

*参数 int timeout:等到timeout毫秒,不管有没获取到锁都返回,timeout为0时,直接返回。

*返回值 true代表获取到锁,false没有获取到

*/

有时候我们会忘记释放锁,Qt还为我们提供了管理锁的类QMutexLocker

QMutex mutex;

void func()

{

QMutexLocker locker(_mutex); //QMutexLocker最好实例化成栈对象,释放之前将QMutex解锁。

}

以上例子,一旦func()执行完成locker自动释放,释放之前先解锁。

3.2、信号量

​互斥锁保护的资源同一时刻只能有一个线程能够获取使用权,有些资源是可以限定多个线程同时访问,那么这个时候可以使用信号量。在Qt中信号量为QSemaphore。

QSemaphore sem(2) //初始化信号量为2

sem.acquire(); //信号量部位0的时候,调用这个函数会让信号量-1,一旦信号量为零,阻塞等待

semaphore.release(); //使信号量+1

4、Qt定时器QTimer

​定时器可以隔一段时间发出信号,通过接收这个信号来处理一些定时任务,需要注意的是,定时器并没有开启一个新线程。Qt中的定时器是QTimer继承自QObject。

通过QTimer::start()来启动定时器

void start(int msec)

/*

*定时msec毫秒后,发射timeout()信号

*/

通过链接信号timeout()来处理一些定时任务,例如:

#include "dialog.h"

#include

#include

Dialog::Dialog(QWidget *parent)

: QDialog(parent)

{

QTimer *timer = new QTimer(this);

connect(timer, SIGNAL(timeout()), this, SLOT(doSomeThing()));

timer->start(3000);

}

void Dialog::doSomeThing()

{

qDebug() << __FUNCTION__ << endl;

}

Dialog::~Dialog()

{

}

本文主要介绍了Qt多线程类QThread与Qt定时器类QTimer的详细方法与实例,更多关于Qt开发知识请查看下面的相关链接

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34975747/article/details/113012677

智能推荐

k8s安装prometheus并持久化数据_k8s prometheus数据持久化_victoruu的博客-程序员秘密

Prometheus 部署已经完成, 但是由于官方的coreos中没有持久化数据, 没有部署ingress, pod重启后数据就会消失. 所以持久化数据就显得比较重要.前置要求: 已经部署了NFS或者其他存储的K8s集群.PV示意图部署kube-prometheus$ git clone https://github.com/coreos/kube-prometheus.git$ kubectl create -f manifests/setup$ until kubectl g.

select 定时器_awks4002的博客-程序员秘密

在编写程序时,我们经常会用到定时器。首先看看select函数原型如下:1 int select(int nfds, fd_set *readfds, fd_set *writefds, 2 fd_set *exceptfds, struct timeval *timeout);参数说明:slect的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数...

CISCO配置文件的导出和导入(Timed out)问题 踩坑记录_weixin_34013044的博客-程序员秘密

起因:我:这台交换机我要用.武哥:不行,在测试,上面很多配置项都不能动!我。。。。。后来武哥找了台3560交换机让我自己玩去:“拷贝一下交换机配置,就能用了” 使用工具:3CDaemon大致流程: 1 开启3CDaemon,这样你的电脑就能作为一个tftp服务器了,监控udp 69端口 ...

解决在alpine下无法使用数组表达式的问题_alpine linux 数组_banche168的博客-程序员秘密

通过alpine作为基础镜像构建容器镜像已经成为默许的标准,当然,在alpine下也有许多坑需要填,最近在制作镜像时,又遇到了新问题,在脚本中使用数组(1,2,3)时提示如下错误:syntax error: unexpected "("导致出现如上提示错误是部分sh脚本不支持数组如:(1,2,3)模式的格式,而bash版支持在网上查找了些资料,有了一些眉目,只给了报错的原因,没...

密码学及相关理论【转】_yuanchuanghhhh的博客-程序员秘密

[密码学实践][现代密码学理论与实践][刘氏高强度公开加密算法设计原理与装置][应用密码学:协议算法与c源程序][密码编码学与网络安全:原理与实践(第二版)][BigNum Math:加密多精度算法的理论与实现]之学习笔记1.任意大于1而又不是素数的整数称为合数,每个合数都可唯一分解出素数因子,素数也称为质数。2.如果生成所以小于100万德素数,也要使用2000年前的一个算法,由阿基米德的朋友Eratosthenes提出的Eratosthenes筛选法3.欧几里得:存在无穷多个素数,并给出完美证明4.素数在

随便推点

个人QQ空间_congdi7904的博客-程序员秘密

个人QQ空间链接:http://user.qzone.qq.com/58789277 ...

C/C++笔试题 (三)【转】_weixin_30706507的博客-程序员秘密

1、写一个“标准”宏,这个宏输入两个参数并返回较小的一个。.#define Min(X, Y) ((X)&gt;(Y)?(Y):(X))//结尾没有;2、嵌入式系统中经常要用到无限循环,你怎么用C编写死循环。while(1){}或者for(;;)3、关键字static的作用是什么?定义静态变量4、关键字const有什么含意?表示常量不可以修改的变量。5、关键字volatile有什么含意?并举出...

SpringCloud笔记三:服务消费者ribbon和feign和注册中心高可用_ribbon如何实现和注册中心解耦_¥诸葛村夫¥的博客-程序员秘密

SpringCloud笔记三:服务消费者ribbon和feign和注册中心高可用文章目录SpringCloud笔记三:服务消费者ribbon和feign和注册中心高可用常用的服务间调用方式微服务调用之ribbon实战,订单调用商品服务Ribbon负载均衡源码分析调整ribbon的负载均衡策略微服务调用方式feignFeign源码解读以及Feign和Ribbon的选择常用的服务间调用方式RPC调用方式远程过程调用,像调用本地服务一样调用服务器的服务。支持同步,异步调用。客户端和服务端之间建立

python安装依赖_python安装依赖包方法_weixin_39791653的博客-程序员秘密

Python安装包的几种常用方式1). pip安装方式(正常在线安装)2). whl安装方式(离线安装),一般是.whl格式的包3). 源码安装方式(离线安装),tar.gz/egg格式4). easy_install安装方式(用的比较少)不同安装方式具体安装步骤1). pip是一个通用的 Python 包管理工具;提供了对 Python 包的查找、下载、安装、卸载的功能,使用pip安装软件包会自...

html常用标签之文本格式化标签、div和span标签_文本格式化标记_m0_47096412的博客-程序员秘密

一、文本格式化标签在网页中,有时需要为文字设置粗体、斜体或下划线等效果,这时就需要用到HTML中的文本格式化标签,使文字以特殊的方式显示。语义标签加粗&lt;strong&gt;&lt;/strong&gt;或&lt;b&gt;&lt;/b&gt;倾斜&lt;em&gt;&lt;/em&gt;或&lt;i&gt;&lt;/i&gt;删除线&lt;del&gt;&lt;/del&gt;或&lt;s&gt;&lt;/s&gt;下划线&lt;ins&gt;&lt;

循环分页请求_weixin_30662539的博客-程序员秘密

var relationNos = new List&lt;String&gt;(); //有很多条数据var loop = (int)Math.Ceiling(relationNos.Count * 0.1); //除以10 for (int i = 0; i &lt; loop; ++i) {   var order...