13-2_Qt 5.9 C++开发指南_线程同步_QMutex+QMutexLocker(目前较为常用)
文章目录
- 1.线程同步的概念
- 2. 基于互斥量的线程同步
- 3.QMutex实现线程同步源代码
- 3.1 qdicethread.h
- 3.2 qdicethread.cpp
- 3.3 dialog.h
- 3.4 dialog.cpp
- 4.QMutexLocker 实现线程同步源代码
- 4.1 qdicethread.h
- 4.2 qdicethread.cpp
- 4.3 dialog.h
- 4.4 dialog.cpp
1.线程同步的概念
在多线程应用程序中,由于多个线程的存在,线程之间可能需要访问同一个变量,或一个线程需要等待另外一个线程完成某个操作后才产生相应的动作。例如,在上一节的实例 samp13_1 中工作线程产生随机的骰子点数,主线程读取散子点数并显示,主线程需要等待工作线程产生一个新的骰子点数后再读取数据。实例 samp13_1 中使用了信号与槽的机制,在产生新的子数之后通过信号通知主线程读取新的数据。
如果不使用信号与槽机制,QDiceThread 的run()函数变为如下的代码:
void QDiceThread::run()
{m_stop=false;//启动线程时令m_stop=falsem_seq=0;qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的while(!m_stop)//循环主体{if (!m_paused){mutex.lock();m_diceValue=qrand(); //获取随机数m_diceValue=(m_diceValue % 6)+1;m_seq++;mutex.unlock();}msleep(500); //线程休眠100ms}
}
那么,QDiceThread 需要定义公共函数,返回m_diceValue 的值,如:
int QDiceThread::diceValue () {return = m diceValue};
以便在主线程中调用此函数读取骰子的点数。
由于没有信号与槽的关联(信号与槽的关系类似于硬件的中断与中断处理函数),主线程只能采用不断查询的方式主动查询是否有新数据,并读取它。但是在主线程调用 diceValue()读取散子点数时,工作线程可能正在执行 run()函数里修改 m_diceValue 值的语句,即:
m_diceValue=qrand(); //获取随机数m_diceValue=(m_diceValue % 6)+1;m_seq++;
而且这几条语句计算量大,需要执行较长时间。执行这两条语句时不希望被主线程调用的diceValue()函数中断,如果中断,则主线程得到的可能是错误的值。
这种情况下,这样的代码段是希望被保护起来的,在执行过程中不能被其他线程打断,以保证计算结果的完整性,这就是线程同步的概念。
在Qt中,有多个类可以实现线程同步的功能,包括 QMutex、QMutexLocker、QReadWriteLock、QReadLocker、QWriteLocker、QWaitCondition 和QSemaphore。下面将分别介绍这些类的用法。
2. 基于互斥量的线程同步
QMutex和QMutexLocker 是基于互斥量的线程同步类,QMutex定义的实例是个互斥量,QMutex主要提供3个函数。
- lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量。
- unlock():解锁一个互斥量,需要与 lock()配对使用。
- tryLock():试图锁定一个互斥量,如果成功锁定就返回 true; 如果其他线程已经锁定了这个互斥量,就返回 false,但不阻塞程序执行。
使用互斥量,对 QDiceThread 类重新定义,不采用信号与槽机制,而是提供一个函数用于主线程读取数据
。更改后的 QDiceThread 类定义如下:
#ifndef QDICETHREAD_H
#define QDICETHREAD_H//#include <QObject>
#include <QThread>
#include <QMutex>class QDiceThread : public QThread
{Q_OBJECTprivate:QMutex mutex; //互斥量int m_seq=0;//序号int m_diceValue;bool m_paused=true;bool m_stop=false; //停止线程
protected:void run() Q_DECL_OVERRIDE;
public:QDiceThread();void diceBegin();//掷一次骰子void diceEnd();//void stopThread();bool readValue(int *seq, int *diceValue); //用于主线程读取数据的函数
};#endif // QDICETHREAD_H
QDiceThread 类里用QMutex 类定义了一个互斥量变量 mutex。
定义了函数 readValue(),用于外部线程读取掷骰子的次数和点数,传递参数采用指针变量以便一次读取两个数据。
下面是QDiceThread 类中关键的 run()和readValue()函数的实现代码。
void QDiceThread::run()
{m_stop=false;//启动线程时令m_stop=falsem_seq=0;qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的while(!m_stop)//循环主体{if (!m_paused){mutex.lock();m_diceValue=qrand(); //获取随机数m_diceValue=(m_diceValue % 6)+1;m_seq++;mutex.unlock();}msleep(500); //线程休眠100ms}
}
bool QDiceThread::readValue(int *seq, int *diceValue)
{if (mutex.tryLock()){*seq=m_seq;*diceValue=m_diceValue;mutex.unlock();return true;}elsereturn false;
}
在 run()函数中,对重新计算骰子点数和掷骰子次数的 3 行代码用互斥量 mutex 的 lock()和unlock0()进行了保护,这部分代码的执行就不会被其他线程中断。注意,lock()与 unlock()必须配对使用。
在 readValue()函数中,用互斥量 mutex 的 tryLock()和 unlock()进行了保护。如果 tryLock()成功锁定互斥量,读取数值的两行代码执行时不会被中断,执行完后解锁;如果 tyLock()锁定失败函数就立即返回,而不会等待。
原理上,对于两个或多个线程可能会同时读或写的变量应该使用互斥量进行保护,例如QDiceThread 中的变量 m_stop 和 m_paused,在 run()函数中读取这两个变量,要在 diceBegin()、diceEnd()和 stopThread()函数里修改这些值,但是这 3 个函数都只有一条赋值语句,可以认为是原子操作,所以,可以不用锁定保护(这种说法应该放到汇编语言的角度去看)。
定义的互斥量 mutex 相当于一个标牌,可以这样来理解互斥量:列车上的卫生间一次只能进一个人,当一个人尝试进入卫生间就是 lock(),如果有人占用,他就只能等待;等里面的人出来,腾出了卫生间是 unlock(),这个等待的人才可以进入并且锁住卫生间的门,就是 lock(),使用完卫生间之后他再出来时就是 unlock()。
QMutex 需要配对使用 lock()和 unlock()来实现代码段的保护,在一些逻辑复杂的代码段或可能发生异常的代码中,配对就可能出错。
QMutexLocker 是另外个简化了互斥量处理的类。QMutexLocker的构造函数接受一个互斥量作为参数并将其锁定,QMutexLocker 的析构函数则将此互斥量解锁,所以在 QMutexLocker 实例变量的生存期内的代码段得到保护,自动进行互斥量的锁定和解锁。例如,QDiceThread 的 run()函数的代码可以改写如下:
void QDiceThread::run()
{m_stop=false;//启动线程时令m_stop=falsem_seq=0;qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的while(!m_stop)//循环主体{if (!m_paused){
// mutex.lock();QMutexLocker Locker(&mutex);m_diceValue=qrand(); //获取随机数m_diceValue=(m_diceValue % 6)+1;m_seq++;
// mutex.unlock();}msleep(500); //线程休眠100ms}
}
这样定义的 QDiceThread 类,在主程序中只能调用其 readValue()函数来不断读取数值。实例samp13_2采用QMutex 进行线程同步,实例 sampl3_3 采用QMutex和qMutexLocker 进行线程同步,其界面与 samp13_1 完全相同,只是增加了定时器,用于定时主动去读取掷骰子线程的数值。实例程序 samp13_2的 Dialog 类的主要定义如下(省略了一些系统生成的声明):
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QTimer>#include "qdicethread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTprivate:int mSeq,mDiceValue;QDiceThread threadA;QTimer mTimer;//定时器
protected:void closeEvent(QCloseEvent *event);
public:explicit Dialog(QWidget *parent = 0);~Dialog();private slots:void onthreadA_started();void onthreadA_finished();void onTimeOut(); //定期器处理槽函数
...private:Ui::Dialog *ui;
};#endif // DIALOG_H
主要是增加了一个定时器 mTimer 和其时间溢出响应槽函数 onTimeOut(),在 Dialog 的构造函数中将mTimer的timeout 信号与此槽函数关联。
connect(&mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
onTimeOut()函数的主要功能是调用 threadA 的readValue()函数读取数值。定时器的定时周期设置为 100ms,小于 threadA 产生一次新数据的周期(500ms),所以可能读出旧的数据,通过存储的掷骰子的次数与读取的掷散子次数是否不同,判断是否为新数据。onTimeOut()函数的代码如下:
void Dialog::onTimeOut()
{ //定时器到时处理槽函数int tmpSeq=0,tmpValue=0;bool valid=threadA.readValue(&tmpSeq,&tmpValue); //读取数值if (valid && (tmpSeq!=mSeq)) //有效,并且是新数据{mSeq=tmpSeq;mDiceValue=tmpValue;QString str=QString::asprintf("第 %d 次掷骰子,点数为:%d",mSeq,mDiceValue);ui->plainTextEdit->appendPlainText(str);QPixmap pic;QString filename=QString::asprintf(":/dice/images/d%d.jpg",mDiceValue);pic.load(filename);ui->LabPic->setPixmap(pic);}
}
窗口中的几个按钮的代码起到的作用也是类似的,这里不细讲。
3.QMutex实现线程同步源代码
3.1 qdicethread.h
#ifndef QDICETHREAD_H
#define QDICETHREAD_H//#include <QObject>
#include <QThread>
#include <QMutex>class QDiceThread : public QThread
{Q_OBJECTprivate:QMutex mutex; //互斥量int m_seq=0;//序号int m_diceValue;bool m_paused=true;bool m_stop=false; //停止线程
protected:void run() Q_DECL_OVERRIDE;
public:QDiceThread();void diceBegin();//掷一次骰子void diceEnd();//void stopThread();bool readValue(int *seq, int *diceValue); //用于主线程读取数据的函数
};#endif // QDICETHREAD_H
3.2 qdicethread.cpp
#include "qdicethread.h"
#include <QTime>QDiceThread::QDiceThread()
{}void QDiceThread::diceBegin()
{
// mutex.lock();m_paused=false;
// mutex.unlock();
}void QDiceThread::diceEnd()
{
// mutex.lock();m_paused=true;
// mutex.unlock();
}void QDiceThread::stopThread()
{
// mutex.lock();m_stop=true;
// mutex.unlock();
}bool QDiceThread::readValue(int *seq, int *diceValue)
{if (mutex.tryLock()){*seq=m_seq;*diceValue=m_diceValue;mutex.unlock();return true;}elsereturn false;
}void QDiceThread::run()
{m_stop=false;//启动线程时令m_stop=falsem_seq=0;qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的while(!m_stop)//循环主体{if (!m_paused){mutex.lock();m_diceValue=qrand(); //获取随机数m_diceValue=(m_diceValue % 6)+1;m_seq++;mutex.unlock();}msleep(500); //线程休眠100ms}
}
3.3 dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QTimer>#include "qdicethread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTprivate:int mSeq,mDiceValue;QDiceThread threadA;QTimer mTimer;//定时器
protected:void closeEvent(QCloseEvent *event);
public:explicit Dialog(QWidget *parent = 0);~Dialog();private slots:void onthreadA_started();void onthreadA_finished();void onTimeOut(); //定期器处理槽函数void on_btnClear_clicked();void on_btnDiceEnd_clicked();void on_btnDiceBegin_clicked();void on_btnStopThread_clicked();void on_btnStartThread_clicked();private:Ui::Dialog *ui;
};#endif // DIALOG_H
3.4 dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"void Dialog::closeEvent(QCloseEvent *event)
{//窗口关闭时if (threadA.isRunning()){threadA.stopThread();threadA.wait();}event->accept();
}Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{//构造函数ui->setupUi(this);connect(&threadA,SIGNAL(started()),this,SLOT(onthreadA_started()));connect(&threadA,SIGNAL(finished()),this,SLOT(onthreadA_finished()));connect(&mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::onthreadA_started()
{ui->LabA->setText("Thread状态:thread started");
}void Dialog::onthreadA_finished()
{ui->LabA->setText("Thread状态:thread finished");
}void Dialog::onTimeOut()
{ //定时器到时处理槽函数int tmpSeq=0,tmpValue=0;bool valid=threadA.readValue(&tmpSeq,&tmpValue); //读取数值if (valid && (tmpSeq!=mSeq)) //有效,并且是新数据{mSeq=tmpSeq;mDiceValue=tmpValue;QString str=QString::asprintf("第 %d 次掷骰子,点数为:%d",mSeq,mDiceValue);ui->plainTextEdit->appendPlainText(str);QPixmap pic;QString filename=QString::asprintf(":/dice/images/d%d.jpg",mDiceValue);pic.load(filename);ui->LabPic->setPixmap(pic);}
}void Dialog::on_btnClear_clicked()
{//清空文本ui->plainTextEdit->clear();
}void Dialog::on_btnDiceEnd_clicked()
{//暂停掷骰子threadA.diceEnd(); //mTimer.stop();//定时器暂停ui->btnDiceBegin->setEnabled(true);ui->btnDiceEnd->setEnabled(false);
}void Dialog::on_btnDiceBegin_clicked()
{//开始掷骰子threadA.diceBegin();mTimer.start(100); //定时器100读取一次数据ui->btnDiceBegin->setEnabled(false);ui->btnDiceEnd->setEnabled(true);
}void Dialog::on_btnStopThread_clicked()
{//结束线程threadA.stopThread();//结束线程的run()函数执行threadA.wait();//ui->btnStartThread->setEnabled(true);ui->btnStopThread->setEnabled(false);ui->btnDiceBegin->setEnabled(false);ui->btnDiceEnd->setEnabled(false);
}void Dialog::on_btnStartThread_clicked()
{//启动线程mSeq=0;threadA.start();ui->btnStartThread->setEnabled(false);ui->btnStopThread->setEnabled(true);ui->btnDiceBegin->setEnabled(true);ui->btnDiceEnd->setEnabled(false);
}
4.QMutexLocker 实现线程同步源代码
4.1 qdicethread.h
#ifndef QDICETHREAD_H
#define QDICETHREAD_H//#include <QObject>
#include <QThread>
#include <QMutex>class QDiceThread : public QThread
{Q_OBJECTprivate:QMutex mutex; //互斥量int m_seq=0;//序号int m_diceValue;bool m_paused=true;bool m_stop=false; //停止线程
protected:void run() Q_DECL_OVERRIDE;
public:QDiceThread();void diceBegin();//掷一次骰子void diceEnd();//void stopThread();bool readValue(int *seq, int *diceValue); //用于主线程读取数据的函数
};#endif // QDICETHREAD_H
4.2 qdicethread.cpp
#include "qdicethread.h"
#include <QTime>QDiceThread::QDiceThread()
{}void QDiceThread::diceBegin()
{
// mutex.lock();m_paused=false;
// mutex.unlock();
}void QDiceThread::diceEnd()
{
// mutex.lock();m_paused=true;
// mutex.unlock();
}void QDiceThread::stopThread()
{
// mutex.lock();m_stop=true;
// mutex.unlock();
}bool QDiceThread::readValue(int *seq, int *diceValue)
{if (mutex.tryLock()){*seq=m_seq;*diceValue=m_diceValue;mutex.unlock();return true;}elsereturn false;
}void QDiceThread::run()
{m_stop=false;//启动线程时令m_stop=falsem_seq=0;qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的while(!m_stop)//循环主体{if (!m_paused){
// mutex.lock();QMutexLocker Locker(&mutex);m_diceValue=qrand(); //获取随机数m_diceValue=(m_diceValue % 6)+1;m_seq++;
// mutex.unlock();}msleep(500); //线程休眠100ms}
}
4.3 dialog.h
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QTimer>#include "qdicethread.h"namespace Ui {
class Dialog;
}class Dialog : public QDialog
{Q_OBJECTprivate:int mSeq,mDiceValue;QDiceThread threadA;QTimer mTimer;//定时器
protected:void closeEvent(QCloseEvent *event);
public:explicit Dialog(QWidget *parent = 0);~Dialog();private slots:void onthreadA_started();void onthreadA_finished();void onTimeOut(); //定期器处理槽函数void on_btnClear_clicked();void on_btnDiceEnd_clicked();void on_btnDiceBegin_clicked();void on_btnStopThread_clicked();void on_btnStartThread_clicked();private:Ui::Dialog *ui;
};#endif // DIALOG_H
4.4 dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"void Dialog::closeEvent(QCloseEvent *event)
{//窗口关闭时if (threadA.isRunning()){threadA.stopThread();threadA.wait();}event->accept();
}Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{//构造函数ui->setupUi(this);connect(&threadA,SIGNAL(started()),this,SLOT(onthreadA_started()));connect(&threadA,SIGNAL(finished()),this,SLOT(onthreadA_finished()));connect(&mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::onthreadA_started()
{ui->LabA->setText("Thread状态:thread started");
}void Dialog::onthreadA_finished()
{ui->LabA->setText("Thread状态:thread finished");
}void Dialog::onTimeOut()
{ //定时器到时处理槽函数int tmpSeq=0,tmpValue=0;bool valid=threadA.readValue(&tmpSeq,&tmpValue); //读取数值if (valid && (tmpSeq!=mSeq)) //有效,并且是新数据{mSeq=tmpSeq;mDiceValue=tmpValue;QString str=QString::asprintf("第 %d 次掷骰子,点数为:%d",mSeq,mDiceValue);ui->plainTextEdit->appendPlainText(str);QPixmap pic;QString filename=QString::asprintf(":/dice/images/d%d.jpg",mDiceValue);pic.load(filename);ui->LabPic->setPixmap(pic);}
}void Dialog::on_btnClear_clicked()
{//清空文本ui->plainTextEdit->clear();
}void Dialog::on_btnDiceEnd_clicked()
{//暂停掷骰子threadA.diceEnd(); //mTimer.stop();//定时器暂停ui->btnDiceBegin->setEnabled(true);ui->btnDiceEnd->setEnabled(false);
}void Dialog::on_btnDiceBegin_clicked()
{//开始掷骰子threadA.diceBegin();mTimer.start(100); //定时器100读取一次数据ui->btnDiceBegin->setEnabled(false);ui->btnDiceEnd->setEnabled(true);
}void Dialog::on_btnStopThread_clicked()
{//结束线程threadA.stopThread();//结束线程的run()函数执行threadA.wait();//ui->btnStartThread->setEnabled(true);ui->btnStopThread->setEnabled(false);ui->btnDiceBegin->setEnabled(false);ui->btnDiceEnd->setEnabled(false);
}void Dialog::on_btnStartThread_clicked()
{//启动线程mSeq=0;threadA.start();ui->btnStartThread->setEnabled(false);ui->btnStopThread->setEnabled(true);ui->btnDiceBegin->setEnabled(true);ui->btnDiceEnd->setEnabled(false);
}
相关文章:

13-2_Qt 5.9 C++开发指南_线程同步_QMutex+QMutexLocker(目前较为常用)
文章目录 1.线程同步的概念2. 基于互斥量的线程同步3.QMutex实现线程同步源代码3.1 qdicethread.h3.2 qdicethread.cpp3.3 dialog.h3.4 dialog.cpp 4.QMutexLocker 实现线程同步源代码4.1 qdicethread.h4.2 qdicethread.cpp4.3 dialog.h4.4 dialog.cpp 1.线程同步的概念 在多线…...

金融行业选择哪种SSL证书才安全可靠
由于金融领域等网站拥有大量客户的敏感信息,且每天都有大量交易需要进行,涉及到大量的资金问题,当这些机构提供的网络和Web应用程序没有足够的安全措施来阻止黑客窃取数据时,就会出现严重的安全问题。而且由于黑客每天都在开发越来…...

面试总结(三)
1.进程和线程的区别 根本区别:进程是操作系统分配资源的最小单位;线程是CPU调度的最小单位所属关系:一个进程包含了多个线程,至少拥有一个主线程;线程所属于进程开销不同:进程的创建,销毁&…...

青大数据结构【2016】
一、单选 二、简答 3.简述遍历二叉树的含义及常见的方法。 4.简要说明图的邻接表的构成。 按顺序将图G中的顶点数据存储在一维数组中, 每一个顶点vi分别建立一个单链表,单链表关联依附顶点vi的边(有向图为以vi为尾的弧)。 邻接…...

聊聊拉长LLaMA的一些经验
Sequence Length是指LLM能够处理的文本的最大长度,越长,自然越有优势: 更强的记忆性。更多轮的历史对话被拼接到对话中,减少出现遗忘现象 长文本场景下体验更佳。比如文档问答、小说续写等 当今开源LLM中的当红炸子鸡——LLaMA…...

线程池的使用详解
一 使用线程池的好处 池化技术相比大家已经屡见不鲜了,线程池、数据库连接池、Http 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。 线程池提供了一种限制和管理资源(包括执行一个任…...

刷题笔记 day4
力扣 611 有效三角形的个数 首先需要知道如何判断 三个数是否能构成三角形。 假如 存在三个数 a < b < c,如果要构成三角形,需要满足: ab > c ; a c > b ; b c > a ; 任意两个数大于第三个数就可构成三角形。 其实不难…...

Python 2.x 中如何使用flask模块进行Web开发
Python 2.x 中如何使用 Flask 模块进行 Web 开发 引言: 随着互联网的快速发展,Web开发成为了互联网行业中一项非常重要的技术。而在 Python 的Web开发中,Flask框架是一种非常流行的选择。它简单轻巧,灵活易用,适合中小型项目的快…...

spring websocket 调用受权限保护的方法失败
版本 spring-security 5.6.10 spring-websocket 5.3.27 现象 通过AbstractWebSocketHandler实现websocket端点处理器 调用使用PreAuthorize注解的方法报错,无法在SecurityContext中找到认证信息 org.springframework.security.authentication.AuthenticationCred…...

Vue.js2+Cesium 四、模型对比
Vue.js2Cesium 四、模型对比 Cesium 版本 1.103.0,低版本 Cesium 不支持 Compare 对比功能。 Demo 同一区域的两套模型,实现对比功能 <template><div style"width: 100%; height: 100%;"><divid"cesium-container"…...

Linux 之 Vi 编辑器
文章目录 1. vi/vim介绍2. vi/vim使用详解2.1 vi/vim的特点2.2 vi/vim三种编辑模式2.3 文本编辑方式 1. vi/vim介绍 vi编辑器是linux和unix上最基本的文本编辑器,工作在字符模式下。由于不需要图形界面,vi是效率很高的文本编辑器。尽管在linux上也有很多…...

Python超实用!批量重命名文件/文件夹,只需1行代码
大家好,这里是程序员晚枫,之前在小破站给大家分享了一个视频:批量重命名文件。 最近在程序员晚枫的读者群里,发现很多朋友对这个功能很感兴趣,尤其是对下一步的优化:批量重命名文件夹。 这周我利用下班时…...

sqoop
一、bg 可以在关系型数据库和hdfs、hive、hbase之间导数 导入:从RDBMS到hdfs、hive、hbase 导出:相反 sqoop1 和sqoop2 (1.99.x)不兼容,sqoop2 并没有生产的稳定版本, Sqoop1 import原理(导入) 从传统数据库获取元数据信息&…...

PySpark 数据操作(综合案例)
搜索引擎日志分析 要求: 读取文件转换成RDD,并完成: 打印输出:热门搜索时间段(小时精度)Top3打印输出:热门搜索词Top3打印输出:统计黑马程序员关键字在哪个时段被搜索最多将数据转…...

产品经理如何平衡用户体验与商业价值?
近期负责前端产品设计工作的小李忍不住抱怨:公司总是要求客户第一,实现客户良好体验,但在实际操作过程中,面向用户 体验提升的需求,研发资源计划几乎很难排上,资源都放在公司根据业务价值排序的需求…...

【PostgreSQL】系列之 一 CentOS 7安装PGSQL15版本(一)
目录 一、何为PostgreSQL? 二、PostgreSQL安装 2.1安装依赖 2.2 执行安装 2.3 数据库初始化 2.4 配置环境变量 2.5 创建数据库 2.6 配置远程 2.7 测试远程 三、常用命令 四、用户创建和数据库权限 一、何为PostgreSQL? PostgreSQL是以加州大学…...

Nginx解决文件服务器文件名显示不全的问题
Nginx可以搭建Http文件服务器,但默认的搭建会长文件名显示不全,比如如下: 问题:显示不全,出现...,需要进行解决 这里使用重新编绎nginx的方式,见此文: https://unix.stackexchange…...

IO进程线程第四天(8.1)
作业1: 从终端获取一个文件的路径以及名字。 若该文件是目录文件,则将该文件下的所有文件的属性显示到终端,类似ls -l该文件夹 若该文件不是目录文件,则显示该文件的属性到终端上,类似ls -l这单个文件 #include<…...

WAF绕过-权限控制篇-后门免杀
WAF绕过主要集中在信息收集,漏洞发现,漏洞利用,权限控制四个阶段。 1、什么是WAF? Web Application Firewall(web应用防火墙),一种公认的说法是“web应用防火墙通过执行一系列针对HTTP/HTTPS的安…...

LED灯的驱动,GPIO子系统,添加按键的中断处理
1.应用程序发送指令控制LED亮灭 2.按键1 按下,led1电位反转 按键2按下,led2电位反转 按键3 按下,led3电位反转 驱动程序: #include <linux/init.h> #include <linux/module.h> #include<linux/of.h> #include…...

Gradle和Maven的区别
Gradle和Maven 当涉及到构建和管理项目时,Gradle和Maven是两个非常流行的选项。本文将讨论Gradle和Maven之间的区别以及它们的配置信息差异。 1. Gradle和Maven的区别 1.1 构建脚本语言 Maven使用XML作为构建脚本语言,而Gradle使用基于Groovy的DSL&…...

C#中 使用yield return 优化大数组或集合的访问
概要 我们在开发过程中,经常需要在一个很大的数组或集合中搜索元素,以满足业务需求。 本文主要介绍通过使用yield return的方式,避免将大量数据全部加载进入内存,再进行处理。从而提高程序的性能。 设计和实现 基本业务场景&a…...

ROS实现导航中止(pub命令版+C++代码版)
pub命令 rostopic pub /move_base/cancel actionlib_msgs/GoalID -- {}C代码: stop_navigation.cpp #include <ros/ros.h> #include <geometry_msgs/Twist.h> #include <nav_msgs/Odometry.h> #include <sys/time.h> #include <unistd…...

【VTK】读取一个 STL 文件,并使用 Qt 显示出来,在 Windows 上使用 Visual Studio 配合 Qt 构建 VTK
知识不是单独的,一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏:Visual Studio。 文章目录 A.hA.cppRef. 直接先把效果放出来,有需要就往下看。 A.h // A.h #pragma once#include <QtWidgets/QMainWindow> #include "…...

数据结构--基础知识
数据结构是什么? 数据结构是计算机科学中研究数据组织、存储和管理的方法和原则。它涉及存储和操作数据的方式,以便能够高效地使用和访问数据。 相关内容 基本组成 数组(Array):数组是一种线性数据结构,…...

天工开物 #7 Rust 与 Java 程序的异步接口互操作
许多语言的高性能程序库都是建立在 C/C 的核心实现上的。 例如,著名 Python 科学计算库 Pandas 和 Numpy 的核心是 C 实现的,RocksDB 的 Java 接口是对底层 C 接口的封装。 Rust 语言的基本目标之一就是替代 C 在这些领域的位置,为开发者提供…...

python实现视频转GIF动图(无水印,包含代码详解和.exe执行文件)
该代码提供了一个简单的界面,允许用户将视频转换为GIF动画,并且可以自定义采样率、GIF帧率和输出大小。修改后的代码将视频帧的大小调整为固定的尺寸,并在生成GIF动画时保持这个尺寸。 一、核心代码实现 核心代码如下: import c…...

一套AI+医疗模式的医院智慧导诊系统源码:springboot+redis+mybatis plus+mysql
一套AI医疗模式的医院智慧导诊系统源码 相关技术: 技术架构:springbootredismybatis plusmysqlRocketMQ 开发语言:java 开发工具:IDEA 前端框架:Uniapp 后端框架:springboot 数 据 库:mys…...

Android 使用modbus协议与可能遇到的问题解决一览
目录 前言一、导入模块二、协议相关1. CRC162. ByteUtil3. ModbusError4. ModbusErrorType5. ModbusFunction6. ModbusRtuMaster7. ByteArrayWriter8. ModbusRtuSerialPortUtil9. ModbusRtuMasterHelp 三、使用总结 前言 本篇文章主要演示android的串口通讯功能,其…...

Virtualbox虚拟机中Ubuntu忘记密码
1、首先重新启动Ubuntu系统,鼠标快速点一下Virtualbox虚拟机窗口获取焦点,然后按住shift键,以调出grub启动菜单。 2、根据提示按下键盘E键进入编辑模式,向下移动光标,将如下"ro quiet splash $vt_handoff"部…...