QT 将单线程改为线程池 端口扫描3.5

news/2025/1/11 13:59:55/

接上篇QT实现 端口扫描暂停和继续功能 3-CSDN博客

多线程与线程池的关系

  • 多线程是基础: 线程池是基于多线程的概念实现的。线程池内部使用多个线程来并发执行任务。
  • 线程池优化多线程: 线程池通过复用线程和管理任务来优化多线程的使用,减少了线程创建和销毁的开销,提高了系统的性能和响应速度。
  • 任务调度: 在多线程环境中,开发者需要手动管理线程的创建、调度和销毁,而使用线程池可以简化这一过程,开发者只需提交任务,线程池会自动处理线程的分配和执行。

1. 修改 MyThread 类为 ScanTask 类

将 MyThread 类重命名为 ScanTask,并使其继承自 QRunnable。这样可以将每个端口扫描任务作为一个独立的任务提交到线程池中。    

ScanTask.h
#ifndef SCANTASK_H  
#define SCANTASK_H  #include <QTcpSocket>  
#include <QDebug>  
#include <QRunnable>  
#include <QObject>  class ScanTask : public QObject, public QRunnable // 需要继承两个类  
{  Q_OBJECT  
public:  ScanTask(QString strIP, int intPort);  ~ScanTask();  void setPaused(bool paused); // 设置暂停状态  void closeThread(); // 关闭线程  protected:  void run() override; // 重写 run 方法  private:  volatile bool isStop; // 控制任务停止  volatile bool isPaused; // 控制任务暂停  QString m_strIP; // IP 地址  int m_intPort; // 端口号  signals:  void send_scan_signal(int port, bool isOpen); // 发送扫描结果信号  void finished(int port, bool isOpen); // 扫描完成信号  
};  #endif // SCANTASK_H
ScanTask.cpp
#include "scantask.h"  ScanTask::ScanTask(QString strIP, int intPort)  : m_strIP(strIP), m_intPort(intPort), isStop(false), isPaused(false) // 初始化成员变量  
{  
}  ScanTask::~ScanTask()  
{  qDebug("ScanTask deleted.");  
}  void ScanTask::setPaused(bool paused)  
{  isPaused = paused; // 更新暂停状态  
}  void ScanTask::closeThread()  
{  isStop = true; // 设置为停止状态  
}  void ScanTask::run()  
{  QTcpSocket socket;  socket.abort(); // 取消任何现有连接  // 执行端口扫描  if (isStop) return; // 检查是否停止  socket.connectToHost(m_strIP, m_intPort); // 连接到指定的 IP 和端口  if (socket.waitForConnected(1000)) // 等待连接  {  emit send_scan_signal(m_intPort, true); // 发送信号,表示端口打开  qDebug("%d: %s", m_intPort, "opened"); // 调试信息  }  else  {  emit send_scan_signal(m_intPort, false); // 发送信号,表示端口关闭  qDebug("%d: %s", m_intPort, "closed");  }  
}

2. 修改 MainWindow 类以使用线程池

在 MainWindow 类中,我们将使用 QThreadPool 来管理 ScanTask 的实例,并在扫描按钮点击事件中提交任务。

MainWindow.h
#ifndef MAINWINDOW_H  
#define MAINWINDOW_H  #include <QMessageBox>  
#include <QTreeWidget>  
#include <QMainWindow>  
#include <QThreadPool>  
#include "scantask.h"  QT_BEGIN_NAMESPACE  
namespace Ui { class MainWindow; }  
QT_END_NAMESPACE  class MainWindow : public QMainWindow  
{  Q_OBJECT  
public:  MainWindow(QWidget *parent = nullptr);  ~MainWindow();  
protected:  
private:  Ui::MainWindow *ui;  QTreeWidgetItem *itemRoot; // 树形控件的条目,用于表示目标 IP  QTreeWidgetItem *itemLeaf; // 树形控件的条目,用于表示目标端口  QThreadPool *threadpool; // 声明一个线程池  bool isPaused; // 控制暂停状态  
private slots:  void on_pushButton_Scan_clicked();  void on_stopButton_clicked();  void on_pushButton_Quit_clicked();  void recv_result(int port, bool isOpen); // 自定义槽函数,用于接收线程发送的消息  void on_pushButton_Stop_clicked(); // 暂停按钮点击事件  void on_pushButton_Continue_clicked(); // 继续按钮点击事件  
};  #endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"  
#include "ui_mainwindow.h"  MainWindow::MainWindow(QWidget *parent)  : QMainWindow(parent)  , ui(new Ui::MainWindow)  
{  ui->setupUi(this);  // 给 QTreeWidget 初始化表头  QStringList head;  head << "扫描结果";  ui->treeWidget->setHeaderLabels(head);  threadpool = new QThreadPool; // 创建线程池  threadpool->setMaxThreadCount(10); // 设置最大线程数  isPaused = false; // 初始化暂停状态  
}  MainWindow::~MainWindow()  
{  delete ui;  
}  void MainWindow::on_pushButton_Scan_clicked()  
{  ui->pushButton_Scan->setEnabled(false); // 正在扫描时,禁用扫描按钮  QString strIP = ui->lineEdit_IP->text(); // 获取用户输入的 IP 地址  int startPort = ui->spinBox_Port->value(); // 获取用户输入的起始端口  int endPort = startPort + 10; // 假设扫描 10 个端口  // 验证 IP 地址是否为空  if (strIP.isEmpty()) {  QMessageBox::information(this, "Error", "请输入 IP", QMessageBox::Ok);  return; // 退出函数  }  ui->treeWidget->clear(); // 清空 QTreeWidget 中的先前结果  itemRoot = new QTreeWidgetItem(ui->treeWidget, QStringList(strIP)); // 创建根节点,显示 IP 地址  for (int i = startPort; i < endPort; i++) { // 扫描指定范围的端口  ScanTask *task = new ScanTask(strIP, i); // 创建新的扫描任务  connect(task, &ScanTask::send_scan_signal, this, &MainWindow::recv_result); // 连接信号  threadpool->start(task); // 将任务提交到线程池  QThread::msleep(10); // 短暂休眠,避免过快提交任务  }  
}  void MainWindow::recv_result(int port, bool isOpen)  
{  QString strPort = QString::number(port); // 将端口号转换为字符串  // 根据端口状态创建叶子节点  if (isOpen)  itemLeaf = new QTreeWidgetItem(itemRoot, QStringList(strPort + " opened")); // 端口打开  else  itemLeaf = new QTreeWidgetItem(itemRoot, QStringList(strPort + " closed")); // 端口关闭  ui->treeWidget->expandAll(); // 展开所有项  
}  void MainWindow::on_stopButton_clicked()  
{  // 停止所有正在运行的任务  threadpool->clear(); // 清理线程池中未执行完的任务  ui->pushButton_Scan->setEnabled(true); // 重新启用扫描按钮  
}  void MainWindow::on_pushButton_Quit_clicked()  
{  QApplication::quit(); // 退出应用程序  
}  void MainWindow::on_pushButton_Stop_clicked()  
{  isPaused = true; // 设置为暂停状态  ui->pushButton_Stop->setEnabled(false); // 禁用暂停按钮  ui->pushButton_Continue->setVisible(true); // 显示继续按钮  
}  void MainWindow::on_pushButton_Continue_clicked()  
{  isPaused = false; // 设置为继续状态  ui->pushButton_Stop->setEnabled(true); // 启用暂停按钮  ui->pushButton_Continue->setVisible(false); // 隐藏继续按钮  
}

原理记录

信号发出和槽的调用

  • 当 ScanTask 对象(task)在其 run() 方法中完成端口扫描后,会发出 send_scan_signal 信号,传递端口号和状态(打开或关闭)。
  • 由于 send_scan_signal 信号与 recv_result 槽函数之间建立了连接,Qt 会自动调用 recv_result 槽函数,并将信号中传递的参数(端口号和状态)传递给它。

线程池的自带功能

  • 自动管理线程QThreadPool 会根据当前正在运行的线程数和最大线程数的设置,自动管理线程的创建和销毁。当您将任务提交到线程池时,线程池会检查当前的线程使用情况。
  • 启动新线程: 如果当前运行的线程数少于您设置的最大线程数(在您的代码中是 10),线程池会自动启动新的线程来执行提交的任务。
  • 任务调度: 线程池会根据任务的提交顺序和线程的可用性来调度任务的执行。它会尽量保持线程的高效利用,同时避免过多的线程导致资源竞争和上下文切换的开销。


http://www.ppmy.cn/news/1562247.html

相关文章

Unity 人体切片三维可视化,可任意裁切切割。查看不同断层的图像。

Unity 人体切片三维可视化&#xff0c;真彩色&#xff0c;可任意裁切切割。查看不同断层的图像。 点击查看效果: 视频效果

css中的部分文字特性

文章目录 一、writing-mode二、word-break三、word-spacing;四、white-space五、省略 总结归纳常见文字特性&#xff0c;后续补充 一、writing-mode 默认horizontal-tbwriting-mode: vertical-lr; 从第一排开始竖着排&#xff0c;到底部再换第二排&#xff0c;文字与文字之间从…

iOS - Tagged Pointer

1. 基本结构 // Tagged Pointer 的内存布局 union TaggedPointer {uintptr_t bits; // 完整的指针值struct {uintptr_t data : 60; // 数据部分uintptr_t tag : 4; // 类型标记};// 扩展类型struct {uintptr_t extData : 52; // 扩展数据uintptr_t extTag : …

朝天椒USB服务器在银泰证券虚拟化超融合场景的应用案例

在数字化浪潮席卷金融行业的今天&#xff0c;银泰证券作为业内知名的金融机构&#xff0c;始终致力于提升业务运营效率与数据安全性。面对虚拟化超融合场景下各种认证U盾的管理挑战&#xff0c;银泰证券选择了朝天椒USB服务器作为其解决方案&#xff0c;成功实现了U盾在虚拟机中…

pytest 参数介绍

命令行参数描述常见使用案例-v / --verbose显示每个测试用例的详细信息&#xff0c;包括测试名称和状态pytest -v-s / --captureno禁用输出捕获&#xff0c;允许 print() 输出显示pytest -s-q / --quiet安静模式&#xff0c;减少输出&#xff0c;仅显示每个测试的通过/失败结果…

电脑硬盘系统迁移及问题处理

一、系统迁移准备 1、确认你的电脑主板是否支持安装两块硬盘,如电脑主板有多个M2硬盘接口,我们将新硬盘安装到主板上,原来的老硬盘安装在第二个接口上,主板只有一个M2接口的话可以使用移动硬盘盒。 2、新硬盘安装好后,我们进入原来的系统,在 此电脑–右键–管理–磁盘管…

3.python基础语法-上

文章目录 1.常量和表达式2.变量和类型2.1.变量的语法2.1.1定义变量2.2.2使用变量 2.2变量类型2.2.1整数2.2.2浮点数(小数)2.2.3字符串2.2.4布尔2.2.5其他 2.3为什么要有这么多类型?2.4动态类型特性 3.注释3.1注释是什么3.2注释的语法3.2.1注释行3.2.2文档字符串 3.3注释的规范…

Python基于YOLOv8和OpenCV实现车道线和车辆检测

使用YOLOv8&#xff08;You Only Look Once&#xff09;和OpenCV实现车道线和车辆检测&#xff0c;目标是创建一个可以检测道路上的车道并识别车辆的系统&#xff0c;并估计它们与摄像头的距离。该项目结合了计算机视觉技术和深度学习物体检测。 1、系统主要功能 车道检测&am…