QT线程 (8)

QT的线程


文章目录

  • QT的线程
  • 1、为什么要用线程?
  • 2、QT线程
    • 1. QT4线程
    • 1. QT5线程![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2a9e78f4a64b47f9b516bb02331e9022.png)
    • 3. QT5线程的退出
    • 4. connect的第五个参数
      • 1. 直接方式
      • 2. 队列方式
  • 3、QT线程绘制QImage图片然后给到主线程显示刷新。
  • 4、多线程使用过程中注意事项
  • 5.总结


1、为什么要用线程?

看如下的dome:
ui的界面如下:
在这里插入图片描述
mywidget.h

#include <QWidget>
#include <QTimer>QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACEclass MyWidget : public QWidget
{Q_OBJECTpublic:MyWidget(QWidget *parent = nullptr);~MyWidget();QTimer *ptime;
private slots:void on_pushButton_clicked();void TimeDealSlot(void);private:Ui::MyWidget *ui;
};

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);ptime = new QTimer(this);connect(ptime, &QTimer::timeout, this, &MyWidget::TimeDealSlot);}MyWidget::~MyWidget()
{delete ui;
}void MyWidget::TimeDealSlot(void)
{static int i = 0;i++;ui->lcdNumber->display(i);
}void MyWidget::on_pushButton_clicked()
{/* 启动定时器 */ptime->start(1000);/* 这个6s的延时当做是很复杂的数据处理 */QThread::sleep(6);}

按下开始按钮启动定时器然后lcdNumber每隔一秒加1显示。但是后面有6s的延时的操作。其结果并不是我们想着这样。
真实的结果是6s执行完后lcdNumber每隔一秒才加1显示。因为在6s的延时时。都在处理这个延时。其它事情没法处理。
只能一件一件做完。当然如果每件事情都不是很复杂那也不会有影响。但这个复杂的6s延时是会有影响的。所以对于很复杂的
数据处理就需要另外开启一个线程。让这个线程来处理。

线程的优势:
在这里插入图片描述
多线程程序有以下几个特点:
在这里插入图片描述

2、QT线程

1. QT4线程

  1. 定义一个类继承与QThread。
  2. 在类里面重写void run()的虚函数(这个run就是线程处理函数并且run的访问限定符是protected。
  3. start()启动线程函数run。注意不能直接调用run()函数。
  4. 线程的退出Thread->quit() pThread->wait();
    模拟的流程如下:
    在这里插入图片描述
    mywidget.h
#include <QWidget>
#include <QTimer>
#include "mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE
class MyWidget : public QWidget
{Q_OBJECT
public:MyWidget(QWidget *parent = nullptr);~MyWidget();QTimer *ptime;Mythread *pThread;
private slots:void on_pushButton_clicked();void TimeDealSlot(void);void WidgetSlot(void);
private:Ui::MyWidget *ui;
};

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);ptime = new QTimer(this);pThread = new Mythread(this);/* 定时器溢出信号timeout */connect(ptime, &QTimer::timeout, this, &MyWidget::TimeDealSlot);/* 当按窗口右上角x时,窗口触发destroyed()信号 */connect(this, &MyWidget::destroyed, this, &MyWidget::WidgetSlot);}
MyWidget::~MyWidget()
{delete ui;
}void MyWidget::TimeDealSlot(void)
{static int i = 0;i++;ui->lcdNumber->display(i);
}
void MyWidget::WidgetSlot(void)
{/* 停止线程 */pThread->quit();/* 等待线程处理完手头动作 */pThread->wait();
}
void MyWidget::on_pushButton_clicked()
{/* 如果定时器没有工作 */if(ptime->isActive() == false) {/* 启动定时器 */ptime->start(1000);}/* 启动线程 不能直接调run函数 */pThread->start();
}

mythread.h

#include <QThread>
class Mythread : public QThread
{Q_OBJECT
public:explicit Mythread(QObject *parent = nullptr);protected://QThread的虚函数//线程处理函数//不能直接调用,通过start()间接调用void run();
signals:};

mythread.cpp

#include "mythread.h"Mythread::Mythread(QObject *parent): QThread{parent}
{}void Mythread::run()
{/* 这个6s的延时当做是很复杂的数据处理 */QThread::sleep(6);
}

当开启一个线程并且把这个延时放到线程里处理。按下开始按钮启动定时器然后lcdNumber每隔一秒加1显示。这样就不会有影响。

1. QT5线程在这里插入图片描述

ui的界面
在这里插入图片描述

  1. 创建一个类继承与QObject
  2. 在类中定义一个线程处理函数(有且只有一个)
#include <QObject>class mythread : public QObject
{Q_OBJECT
public:explicit mythread(QObject *parent = nullptr);/* 自定义线程处理函数 */void ThreadDeal(void);
};
  1. 在主线程里定义自定义线程与 子线程。并且自定义线程不能指定父对象。指定了会报一个错误。
QThread *pThread; /* 子线程 */
mythread *pMyThread; /* 自定义线程 */
//动态分配空间,不能指定父对象
pMyThread = new mythread;
//创建子线程
pThread = new QThread(this);
  1. 把自定义线程加入到子线程。
    //把自定义线程加入到子线程中  子线程 与 自定义线程建立联系pMyThread->moveToThread(pThread);
  1. 启动子线程,但是没有启动线程处理函数
    //启动子线程,但是没有启动线程处理函数pThread->start();
  1. 在启动子线程后发送一个自定义信号。通过信号与槽去启动线程处理函数。直接调用启动是没有用的。
//只能通过 signal - slot 方式调用
emit startThread(); /* 发送信号 */
/* 接收信号去启动线程处理函数 */
connect(this, &MyWidget::startThread, pMyThread, &mythread::myTimeout);
  1. 线程退出
    pThread->quit();pThread->wait();

总体的流程:
在这里插入图片描述
mywidget.h

#include <QWidget>
#include <QThread>
#include "mythread.h"
#include <QTimer>QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACEclass MyWidget : public QWidget
{Q_OBJECTpublic:MyWidget(QWidget *parent = nullptr);~MyWidget();QThread *pThread; /* 子线程 */mythread *pMyThread; /* 自定义线程 */QTimer *ptime;private slots:void on_pushButtonStart_clicked();void on_pushButtonStop_clicked();void WidgetSlot(void);void TimeDealSlot(void);signals:void StartThreadDealSignal();private:Ui::MyWidget *ui;
};

mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);ptime = new QTimer(this);/* 自定义线程 不能指定父对象 */pMyThread = new mythread;/* 创建子线程 */pThread = new QThread(this);/* 把自定义线程加入到子线程 */pMyThread->moveToThread(pThread);/* 定时器溢出信号timeout */connect(ptime, &QTimer::timeout, this, &MyWidget::TimeDealSlot);/* 当按窗口右上角x时,窗口触发destroyed()信号 */connect(this, &MyWidget::destroyed, this, &MyWidget::WidgetSlot);/* 信号与槽去调用线程处理函数 */connect(this, &MyWidget::StartThreadDealSignal, pMyThread, &mythread::ThreadDeal);
}MyWidget::~MyWidget()
{delete ui;
}void MyWidget::TimeDealSlot(void)
{static int i = 0;i++;ui->lcdNumber->display(i);
}
void MyWidget::on_pushButtonStart_clicked()
{/* 线程启动已经开启就退出 */if(pThread->isRunning() == true) {return;}qDebug() << "主线程号:" << QThread::currentThread();/* 如果定时器没有工作 */if(ptime->isActive() == false) {/* 启动定时器 */ptime->start(1000);}/* 启动子线程 但并没有启动线程处理函数 */pThread->start();/* 发送StartThreadDealSignal信号启动线程处理函数 */emit StartThreadDealSignal();
}void MyWidget::WidgetSlot(void)
{on_pushButtonStop_clicked();delete pMyThread;
}void MyWidget::on_pushButtonStop_clicked()
{/* 线程启动已经停止就退出 */if(pThread->isRunning() == false) {return;}/* 停止线程 */pThread->quit();/* 等待线程处理完手头动作 */pThread->wait();
}

mythread.h

#include <QObject>class mythread : public QObject
{Q_OBJECT
public:explicit mythread(QObject *parent = nullptr);/* 自定义线程处理函数 */void ThreadDeal(void);signals:};

mythread.cpp

#include "mythread.h"
#include<QThread>
#include<QDebug>
mythread::mythread(QObject *parent): QObject{parent}
{}void mythread::ThreadDeal(void)
{/* 这个6s的延时当做是很复杂的数据处理 */QThread::sleep(6);qDebug() << "子线程号:" << QThread::currentThread();
}

3. QT5线程的退出

pThread->quit()是会等到线程手头的工作处理完才退出。但如果线程的工作处理不完。比如死循环。那怎么退出呢?

    /* 这个6s的延时当做是很复杂的数据处理 */while(1) {QThread::sleep(6);qDebug() << "子线程号:" << QThread::currentThread();}
  1. 强制退出
    用pThread->terminate()代替pThread->quit。但是这样可能会导致内存问题。强制退出风险很大。
  2. 在自定义类里定义一个标志位
    mythread.h
    void setThreadQuitFlag(int flag); /* 设置标志位函数 */int ThreadQuitFlag;  // 定义一个标志位

mythread.cpp

#include "mythread.h"
#include<QThread>
#include<QDebug>
mythread::mythread(QObject *parent): QObject{parent}
{ThreadQuitFlag = false;
}void mythread::ThreadDeal(void)
{/* 这个6s的延时当做是很复杂的数据处理 */while(1) {QThread::sleep(6);qDebug() << "子线程号:" << QThread::currentThread();if (ThreadQuitFlag == true) {break; // 退出线程}}}
void mythread::setThreadQuitFlag(int flag)
{ThreadQuitFlag = flag;
}

在主线程里一开始启动线程设置标志为

pMyThread->setThreadQuitFlag(false);

在主线程里结束线程设置标志为true就可以退出线程

pMyThread->setThreadQuitFlag(true);

4. connect的第五个参数

只介绍队列与直接方式。多线程时才有意义。
如果是多线程,默认使用队列方式。
如果是单线程, 默认使用直接方式。
队列: 槽函数所在的线程和接收者一样。
直接:槽函数所在线程和发送者一样。

1. 直接方式

在如上QT5线程的dome中设置第五个参数 Qt::DirectConnection

    /* 信号与槽去调用线程处理函数 */connect(this, &MyWidget::StartThreadDealSignal, pMyThread, &mythread::ThreadDeal, Qt::DirectConnection);

在这里插入图片描述
槽函数所在线程和发送者(主窗口)一样。

2. 队列方式

在如上QT5线程的dome中设置第五个参数 Qt::DirectConnection

    /* 信号与槽去调用线程处理函数 */connect(this, &MyWidget::StartThreadDealSignal, pMyThread, &mythread::ThreadDeal, Qt::QueuedConnection);

在这里插入图片描述
队列: 槽函数所在的线程和接收者(子线程)一样。

3、QT线程绘制QImage图片然后给到主线程显示刷新。

具体的流程:
1.点击绘图按钮调用线程处理函数
2. 线程里绘图图片但是不显示。绘制一次刷新一次。发送图片给主线程
3. 主线程拿到子线程的图片。在主窗口刷新显示。
伪代码如下:
在这里插入图片描述
注意:QImage才能在线程里绘图。
ui界面如下:就一个按钮
在这里插入图片描述

widget.h

#include <QWidget>
#include "mythread.h"
#include <QThread>
#include <QImage>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();QTimer *pTime;//重写绘图事件void paintEvent(QPaintEvent *);
public slots:void getImage(QImage); //槽函数void dealClose(); //窗口关闭槽函数private:Ui::Widget *ui;QImage image;MyThread *myT; //自定义线程对象QThread *thread; //子线程
};

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QThread>Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);this->resize(600,600);//自定义类对象,分配空间,不可以指定父对象myT = new MyThread;//创建子线程thread = new QThread(this);//把自定义模块添加到子线程myT->moveToThread(thread);//启动子线程,但是,并没有启动线程处理函数thread->start();//线程处理函数,必须通过signal - slot 调用connect(ui->pushButton, &QPushButton::pressed, myT, &MyThread::drawImage);connect(myT, &MyThread::updateImage, this, &Widget::getImage);connect(this, &Widget::destroyed, this, &Widget::dealClose);}Widget::~Widget()
{delete ui;
}void Widget::dealClose()
{//退出子线程thread->quit();//回收资源thread->wait();delete myT;}void Widget::getImage(QImage temp)
{image = temp;update(); //更新窗口,间接调用paintEvent()
}void Widget::paintEvent(QPaintEvent *)
{QPainter p(this); //创建画家,指定绘图设备为窗口p.drawImage(50, 50, image);
}

mythread.h

#include <QObject>
#include <QImage>class MyThread : public QObject
{Q_OBJECT
public:explicit MyThread(QObject *parent = 0);//线程处理函数void drawImage();signals:void updateImage(QImage temp);public slots:
};

mythread.cpp

#include "mythread.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QImage>
#include <QRandomGenerator>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}
void MyThread::drawImage()
{//定义QImage绘图设备QImage image(500, 500, QImage::Format_ARGB32); // QImage::Format_ARGB32透明的颜色//定义画家,指定绘图设备QPainter p(&image);//定义画笔对象QPen pen;pen.setWidth(5); //设置宽度//把画笔交给画家p.setPen(pen);//定义画刷QBrush brush;brush.setStyle(Qt::SolidPattern); //设置样式brush.setColor(QColor(QRandomGenerator::global()->bounded(0,255),QRandomGenerator::global()->bounded(0,255),QRandomGenerator::global()->bounded(0,255))); //随机设置颜色//把画刷交给画家p.setBrush(brush);//定义点QPoint a[] ={QPoint(QRandomGenerator::global()->bounded(0,500), QRandomGenerator::global()->bounded(0,500)),QPoint(QRandomGenerator::global()->bounded(0,500), QRandomGenerator::global()->bounded(0,500)),QPoint(QRandomGenerator::global()->bounded(0,500), QRandomGenerator::global()->bounded(0,500)),QPoint(QRandomGenerator::global()->bounded(0,500), QRandomGenerator::global()->bounded(0,500)),QPoint(QRandomGenerator::global()->bounded(0,500), QRandomGenerator::global()->bounded(0,500)),QPoint(QRandomGenerator::global()->bounded(0,500), QRandomGenerator::global()->bounded(0,500))};/* 绘制多边形 */p.drawPolygon(a, sizeof(a)/sizeof(a[0]));//通过信号发送图片emit updateImage(image);
}

4、多线程使用过程中注意事项

需要移动到子线程中处理的模块类,创建的对象的时候不能指定父对象。但在关闭窗口时要记得释放。

    //自定义类对象,分配空间,不可以指定父对象myT = new MyThread;

线程不能操作UI对象(从Qwidget直接或间接派生的窗口对象)

#include "mythread.h"
#include<QThread>
#include<QDebug>
#include<QMessageBox>
mythread::mythread(QObject *parent): QObject{parent}
{ThreadQuitFlag = false;
}
void mythread::ThreadDeal(void)
{/* 这个6s的延时当做是很复杂的数据处理 */while(1) {QThread::sleep(6);qDebug() << "子线程号:" << QThread::currentThread();if (ThreadQuitFlag == true) {break; // 退出线程}/* 在线程处理函数里操作UI对象 */QMessageBox::about(NULL,"关于","在线程处理函数内部不能操作图形界面");}}

QMessageBox::about(NULL,“关于”,“在线程处理函数内部不能操作图形界面”)
这样会导致程序崩掉。
在这里插入图片描述
在线程处理函数内部不能操作图形界面。只能是纯数据处理。

5.总结

主要介绍QT的线程的两种用法。connect的第五个参数 。以及线程退pThread->terminate()与pThread->quit函数不一样。以及用pThread->quit。如果线程退不出来(死循环)用标志位来退出。还有线程里不能操作图形界面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/250644.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自然语言处理--概率最大中文分词

自然语言处理附加作业--概率最大中文分词 一、理论描述 中文分词是指将中文句子或文本按照语义和语法规则进行切分成词语的过程。在中文语言中&#xff0c;词语之间没有明显的空格或标点符号来分隔&#xff0c;因此需要通过分词工具或算法来实现对中文文本的分词处理。分词的…

调试以及发布npm组件

开发原因&#xff1a; 由于公司自己的封装到npm的组件有点问题&#xff0c;负责人由在忙其他&#xff0c;就由我去负责改改&#xff0c;中途出了不少问题&#xff0c;记录一下。 一、下载源码 第一步肯定是去git上把组件的源码下载下来&#xff0c;这一步没什么好说&#xf…

分布式事务 seata+nacos 部署

分布式事务 seatanacos 部署 一、下载seata二、解压配置三、导入数据库四、nacos配置五、配置要引入事务的模块的配置文件六、启动七、测试 这里使用的版本&#xff1a; nacos&#xff1a;2.0.4 seata&#xff1a;1.5.2 seata官方地址&#xff1a;https://seata.apache.org/zh-…

LabVIEW汽车自燃监测预警系统

LabVIEW汽车自燃监测预警系统 随着汽车行业的飞速发展&#xff0c;汽车安全问题日益受到公众的关注。其中&#xff0c;汽车自燃现象因其突发性和破坏性&#xff0c;成为一个不可忽视的安全隐患。为了有效预防和减少自燃事故的发生&#xff0c;提出了LabVIEW的汽车自燃监测预警…

C++文件操作(1)

C文件操作 1.文本的写入及读取文本文件写入文本文件读取 2.二进制文件的写入及读取二进制文件写入二进制文件读取 3.小结 C也有处理文件的能力&#xff0c;其功能实现依赖文件流。文件流是C中用来处理文件输入输出的一种流类。文件流可以用于从文件中读取数据或将数据写入到文件…

ctfshow web-77

开启环境: 先直接用伪协议获取 flag 位置。 c?><?php $anew DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString(). );} exit(0); ?> 发现 flag36x.txt 文件。同时根目录下还有 readflag&#xff0c;估计需要调用 readflag 获…

RabbitMQ之三种队列之间的区别及如何选型

目录 不同队列之间的区别 Classic经典队列 Quorum仲裁队列 Stream流式队列 如何使用不同类型的队列​ Quorum队列 Stream队列 不同队列之间的区别 Classic经典队列 这是RabbitMQ最为经典的队列类型。在单机环境中&#xff0c;拥有比较高的消息可靠性。 经典队列可以选…

FFMPEG 之 DXVA2 硬解

一&#xff1a;FFMPEG 支持的硬解方式有很多&#xff1a; DXVA2、D3D11VA、CUDA、QSV、OPENCL、DRM、VAAPI、VDPAU、VIDEOTOOLBOX、MEDIACODEC。 有的支持 Windows 平台&#xff0c;有的支持 linux 平台&#xff0c;有的支持 apple ios 平台&#xff0c;…

和鲸科技与智谱AI达成合作,共建大模型生态基座

近日&#xff0c;上海和今信息科技有限公司&#xff08;简称“和鲸科技”&#xff09;与北京智谱华章科技有限公司&#xff08;简称“智谱AI”&#xff09;签订合作协议&#xff0c;双方将携手推动国产通用大模型的广泛应用与行业渗透&#xff0c;并积极赋能行业伙伴探索领域大…

Redis核心技术与实战【学习笔记】 - 16.Redis 缓存异常:缓存和数据库不一致

概述 只要使用 Redis 缓存&#xff0c;就必须面对缓存和数据库的一致性问题。 重要的是&#xff0c;如果数据不一致&#xff0c;那么业务应用从缓存中读取的数据就不是最新数据&#xff0c;这会导致严重的问题。比如说&#xff0c;我们把电商商品的库存信息缓存在 Redis 中&am…

【Tomcat与网络3】Tomcat的整体架构

目录 1.演进1&#xff1a;将连接和处理服务分开 2演进2&#xff1a;Container的演进 3 再论Tomcat的容器结构 4 Tomcat处理请求的过程 5 请求的处理过程与Pipeline-Valve管道 在前面我们介绍了Servlet的基本原理&#xff0c;本文我们结合Tomcat来分析一下如何设计一个大型…

C语言第十七弹---指针(一)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、内存和地址 1.1、内存 2、指针变量和地址 2.1、取地址操作符&#xff08;&&#xff09; 2.2、指针变量和解引用操作符&#xff08;*&#xff09;…

GrayLog踩坑历险记

背景 GrayLog作为ELK的替代产品&#xff0c;是新生代的日志采集框架。在一个采集节点日志的需求中&#xff0c;因为节点很多&#xff0c;产生的日志也很多&#xff0c;因此尝试了使用GrayLog进行日志的采集。下面记录一下使用GrayLog中遇到的坑和解决方案。 一、部署与启动 …

Mysql连接编译踩坑问题

复习下mysql基本命令 登录mysql mysql #没密码登录 mysql -u root -p #以用户名root 需要交互输入密码方式登录显示所有数据库 SHOW DATABASES&#xff1b; 使用某个数据库 use database_name; 查看当前数据库某一张表的表结构 DESCRIBE table_name; 解决一个mysql 用…

汽车软件开发模式的5个特点

汽车软件开发属于较为复杂的系统工程&#xff0c;经常让来自不同知识背景的工程师在观点交锋时出现分歧。在解决复杂性和对齐讨论基准时&#xff0c;可以通过勾勒出讨论对象最关键的几个特征来树立典型概念。本文旨在通过5个典型特点的抽取&#xff0c;来勾勒出汽车软件开发模式…

通过 ChatGPT 的 Function Call 查询数据库

用 Function Calling 的方式实现手机流量包智能客服的例子。 def get_sql_completion(messages, model"gpt-3.5-turbo"):response client.chat.completions.create(modelmodel,messagesmessages,temperature0,tools[{ # 摘自 OpenAI 官方示例 https://github.com/…

一张证书如何改变职业生涯?程序员必看!

随着信息产业的迅猛发展&#xff0c;对行业人才的需求也在逐年扩大。据统计&#xff0c;未来五年&#xff0c;我国对信息化人才的总需求量将高达1500万至2000万人。以软件开发为例&#xff0c;我国对软件人才的需求正以每年20%的速度递增&#xff0c;每年新增的需求接近百万。计…

第五讲:函数与类库

第五讲&#xff1a;函数与类库 第五讲&#xff1a;函数与类库函数定义实参变量的作用域返回值代码复用 类创建和使用类继承导入类 模块与库概念标准库第三方库 第五讲&#xff1a;函数与类库 函数 定义 函数就是代码块&#xff0c;只不过我们给这个代码块特地进行命名&#…

好的问卷设计标准:确保数据质量与准确性的关键要素

问卷的主要由三个部分组成&#xff1a;问卷说明、问卷主题、问卷结束。而这三个部分又包含了很多因素&#xff0c;比如问卷主题、问卷标题、问卷题目、问卷调查对象等。制作问卷不仅仅是简单的问题罗列&#xff0c;然后进行发放。不同质量的调查问卷会反馈出不一样的效果&#…

网络安全全栈培训笔记(60-服务攻防-中间件安全CVE复现WeblogicJenkinsGlassFish)

第60天 服务攻防-中间件安全&CVE复现&Weblogic&Jenkins&GlassFish 知识点: 中间件及框架列表: lIS,Apache,Nginx,Tomcat,Docker,Weblogic,JBoos,WebSphere,Jenkins, GlassFish,Jira,Struts2,Laravel,Solr,Shiro,Thinkphp,Sprng,Flask,jQuery 1、中间件-Web…