当前位置: 首页 > news >正文

Qt5开发及实例V2.0-第十二章-Qt多线程

Qt5开发及实例V2.0-第十二章-Qt多线程

  • 第12章 Qt 5多线程
    • 12.1 多线程及简单实例
    • 12.2 多线程控制
      • 12.2.1 互斥量
      • 12.2.2 信号量
      • 12.2.3 线程等待与唤醒
    • 12.3 多线程应用
      • 12.3.1 【实例】:服务器编程
      • 12.3.2 【实例】:客户端编程
  • 本章相关例程源码下载
    • 1.Qt5开发及实例_CH1201.rar 下载
    • 2.Qt5开发及实例_CH1202.rar 下载
    • 3.Qt5开发及实例_CH1203.rar 下载
    • 4.Qt5开发及实例_CH1204.rar 下载
    • 5.Qt5开发及实例_CH1205.rar 下载

第12章 Qt 5多线程

多线程具有以下几点优势。
(1)提高应用程序的响应速度。这对于开发图形界面的程序尤为重要,当一个操作耗时很长时,整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等的操作,而使用多线程技术可将耗时长的操作置于一个新的线程,从而避免以上的问题。
(2)使多CPU系统更加有效。当线程数不大于CPU数目时,操作系统可以调度不同的线程运行于不同的CPU上。
(3)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于代码的理解和维护。

多线程程序有以下几个特点。
(1)多线程程序的行为无法预期,当多次执行上述程序时,每一次的运行结果都可能不同。
(2)多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关。
(3)多线程的切换可能发生在任何时刻、任何地点。
(4)多线程对代码的敏感度高,因此对代码的细微修改都可能产生意想不到的结果。

12.1 多线程及简单实例

【例】(难度一般)(CH1201)如图12.1所示,单击“开始”按钮将启动数个工作线程(工作线程数目由MAXSIZE宏决定),各个线程循环打印数字0~9,直到单击“停止”按钮终止所有线程为止。
实现步骤如下。
(1)在头文件“threaddlg.h”中声明用于界面显示所需的控件,其具体代码如下:

#include <QDialog>
#include <QPushButton>
class ThreadDlg : public QDialog
{Q_OBJECT
public:ThreadDlg(QWidget *parent = 0);~ThreadDlg();
private:QPushButton *startBtn;QPushButton *stopBtn;QPushButton *quitBtn;
};

(2)在源文件“threaddlg.cpp”的构造函数中,完成各个控件的初始化工作,其具体代码如下:

#include "threaddlg.h"
#include <QHBoxLayout>
ThreadDlg::ThreadDlg(QWidget *parent): QDialog(parent)
{setWindowTitle(tr("线程"));startBtn = new QPushButton(tr("开始"));stopBtn = new QPushButton(tr("停止"));quitBtn = new QPushButton(tr("退出"));QHBoxLayout *mainLayout = new QHBoxLayout(this);mainLayout->addWidget(startBtn);mainLayout->addWidget(stopBtn);mainLayout->addWidget(quitBtn);
}

(3)此时运行程序,界面显示如图12.1所示。
在这里插入图片描述
以上完成了界面的设计,下面的内容是具体的功能实现。
(1)在头文件“workthread.h”中,工作线程WorkThread类继承自QThread类。重新实现run()函数。其具体代码如下:

#include <QThread>
class WorkThread : public QThread
{Q_OBJECT
public:WorkThread();
protected:void run();
};

(2)在源文件“workthread.cpp”中添加具体实现代码如下:

#include "workthread.h"
#include <QtDebug>
WorkThread::WorkThread()
{
}

run()函数实际上是一个死循环,它不停地打印数字0~9。为了显示效果明显,程序将每一个数字重复打印8次。

void WorkThread::run()
{while(true){for(int n=0;n<10;n++)qDebug()<<n<<n<<n<<n<<n<<n<<n<<n;}
}

(3)在头文件“threaddlg.h”中添加以下内容:

#include "workthread.h"
#define MAXSIZE 1			//MAXSIZE宏定义了线程的数目
public slots:void slotStart();				//槽函数用于启动线程void slotStop();				//槽函数用于终止线程
private:
WorkThread *workThread[MAXSIZE];		//(a)

(4)在源文件“threaddlg.cpp”中添加以下内容。
其中,在构造函数中添加如下代码:

connect(startBtn,SIGNAL(clicked()),this,SLOT(slotStart()));
connect(stopBtn,SIGNAL(clicked()),this,SLOT(slotStop()));
connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));

槽函数slotStart(),当用户单击“开始”按钮时,此函数将被调用。这里使用两个循环,目的是为了使新建的线程尽可能同时开始执行,其具体实现代码如下:

void ThreadDlg::slotStart()
{for(int i=0;i<MAXSIZE;i++){workThread[i]=new WorkThread();		//(a)}for(int i=0;i<MAXSIZE;i++){workThread[i]->start();			//(b)}startBtn->setEnabled(false);stopBtn->setEnabled(true);
}

槽函数slotStop(),当用户单击“停止”按钮时,此函数将被调用。其具体实现代码如下:

void ThreadDlg::slotStop()
{for(int i=0;i<MAXSIZE;i++){workThread[i]->terminate();workThread[i]->wait();}startBtn->setEnabled(true);stopBtn->setEnabled(false);
}

(5)运行结果如图12.2所示。
在这里插入图片描述

12.2 多线程控制

实现线程的互斥与同步常使用的类有QMutex、QMutexLocker、QReadWriteLocker、QReadLocker、QWriteLocker、QSemaphore和QWaitCondition。
下面举一个例子来说明问题:

class Key
{
public:Key() {key=0;}int creatKey() {++key; return key;}int value()const {return key;}
private:int key;    
};

虽然类Key产生主键的函数creatKey()只有一条语句执行修改成员变量key的值,但是C++的“++”操作符并不是原子操作,通常编译后,它将被展开成为以下三条机器命令:
 将变量值载入寄存器。
 将寄存器中的值加1。
 将寄存器中的值写回主存。
假设当前的key值为0,如果线程1和线程2同时将0值载入寄存器,执行加1操作并将加1后的值写回主存,则结果是两个线程的执行结果将互相覆盖,实际上仅进行了一次加1操作,此时的key值为1。

12.2.1 互斥量

1.QMutex类
QMutex类是对互斥量的处理。它被用来保护一段临界区代码,即每次只允许一个线程访问这段代码。
QMutex类的lock()函数用于锁住互斥量。如果互斥量处于解锁状态,则当前线程就会立即抓住并锁定它,否则当前线程就会被阻塞,直到持有这个互斥量的线程对它解锁。线程调用lock()函数后就会持有这个互斥量,直到调用unlock()操作为止。
QMutex类还提供了一个tryLock()函数。如果互斥量已被锁定,则立即返回。
例如:

class Key
{
public:Key() {key=0;}int creatKey() { mutex.lock();  ++key;  return key;  mutex. unlock();}int value()const { mutex.lock();  return key;  mutex.unlock();}
private:int key;QMutex mutex;
};

2.QMutexLocker类
Qt提供的QMutexLocker类可以简化互斥量的处理,它在构造函数中接收一个QMutex对象作为参数并将其锁定,在析构函数中解锁这个互斥量,这样就解决了以上问题。
例如:

class Key
{
public:Key() {key=0;}int creatKey() { QmutexLocker locker(&mutex);  ++key;  return key; }int value()const { QmutexLocker locker(&mutex);  return key; }
private:int key;QMutex mutex;
};

12.2.2 信号量

生产者/消费者实例中对同步的需求有两处:
(1)如果生产者过快地生产数据,将会覆盖消费者还没有读取的数据。
(2)如果消费者过快地读取数据,将越过生产者并且读取到一些过期数据。
针对以上问题,可以有两种解决方法:
(1)首先使生产者填满整个缓冲区,然后等待消费者读取整个缓冲区,这是一种比较笨拙的方法。
(2)使生产者和消费者线程同时分别操作缓冲区的不同部分,这是一种比较高效的方法。

【例】(难度一般)(CH1202)基于控制台程序实现。
(1)源文件“main.cpp”中添加的具体实现代码如下:

#include <QCoreApplication>
#include <QSemaphore>
#include <QThread>
#include <stdio.h>
const int DataSize=1000;
const int BufferSize=80;
int buffer[BufferSize];					//(a)
QSemaphore freeBytes(BufferSize);			//(b)
QSemaphore usedBytes(0);				//(c)

(2)Producer类继承自QThread类,作为生产者类,其声明如下:

class Producer : public QThread
{
public:Producer();void run();
};

Producer构造函数中没有实现任何内容:

Producer::Producer()
{
}

Producer::run()函数的具体实现代码如下:

void Producer::run()
{for(int i=0;i<DataSize;i++){freeBytes.acquire();				//(a)buffer[i%BufferSize]=(i%BufferSize);			//(b)usedBytes.release();				//(c)}
}

(3)Consumer类继承自QThread类,作为消费者类,其声明如下:

class Consumer : public QThread
{
public:Consumer();void run();
};

Consumer构造函数中没有实现任何内容:

Consumer::Consumer()
{
}

Consumer::run()函数的具体实现代码如下:

void Consumer::run()
{for(int i=0;i<DataSize;i++){usedBytes.acquire();							//(a)fprintf(stderr,"%d",buffer[i%BufferSize]);	//(b)if(i%16==0&&i!=0)fprintf(stderr,"\n");freeBytes.release();							//(c)}fprintf(stderr,"\n");
}

(4)main()函数的具体内容如下:

int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Producer producer;Consumer consumer;/* 启动生产者和消费者线程 */producer.start();consumer.start();/* 等待生产者和消费者各自执行完毕后自动退出 */producer.wait();consumer.wait();return a.exec();
}

(5)最终运行结果如图12.3所示。
在这里插入图片描述

12.2.3 线程等待与唤醒

【例】(难度一般)(CH1203)使用QWaitCondition类解决生产者和消费者问题。
源文件“main.cpp”的具体内容如下:

#include <QCoreApplication>
#include <QWaitCondition>
#include <QMutex>
#include <QThread>
#include <stdio.h>
const int DataSize=1000;
const int BufferSize=80;
int buffer[BufferSize];
QWaitCondition bufferEmpty;
QWaitCondition bufferFull;
QMutex mutex;				//(a)
int numUsedBytes=0;				//(b)
int rIndex=0;					//(c)

生产者线程Producer类继承自QThread类,其声明如下:

class Producer : public QThread
{
public:Producer();void run();
};

Producer构造函数无须实现:

Producer::Producer()
{
}

Producer::run()函数的具体内容如下:

void Producer::run()
{for(int i=0;i<DataSize;i++)				//(a){mutex.lock();if(numUsedBytes==BufferSize)			//(b)bufferEmpty.wait(&mutex);			//(c)buffer[i%BufferSize]=numUsedBytes;		//(d)++numUsedBytes;						//增加numUsedBytes变量bufferFull.wakeAll();				//(e)mutex.unlock();}
}

其中,
(a) for(int i=0;i<DataSize;i++) { mutex.lock(); … mutex.unlock();}:for循环中的所有语句都需要使用互斥量加以保护,以保证其操作的原子性。
(b) if(numUsedBytes==BufferSize):首先检查缓冲区是否已经填满。
© bufferEmpty.wait(&mutex):如果缓冲区已经填满,则等待“缓冲区有空位”(bufferEmpty变量)条件成立。wait()函数将互斥量解锁并在此等待,其原型如下:

bool QWaitCondition::wait
(QMutex * mutex,unsigned long time = ULONG_MAX
)

(d) buffer[i%BufferSize]=numUsedBytes:如果缓冲区未被填满,则向缓冲区中写入一个整数值。
(e) bufferFull.wakeAll():最后唤醒等待“缓冲区有可用数据”(bufferEmpty变量)条件为“真”的线程。

消费者线程Consumer类继承自QThread类,其声明如下:

class Consumer : public QThread
{
public:Consumer();void run();
};

Consumer构造函数中无须实现内容:

Consumer::Consumer()
{
}

Consumer::run()函数的具体内容如下:

void Consumer::run()
{forever{mutex.lock();if(numUsedBytes==0)bufferFull.wait(&mutex);			//(a)printf("%ul::[%d]=%d\n",currentThreadId(),rIndex,buffer[rIndex]);//(b)rIndex=(++rIndex)%BufferSize;		//将rIndex变量循环加1--numUsedBytes;				//(c)bufferEmpty.wakeAll();			//(d)mutex.unlock();}printf("\n");
}

main()函数的具体内容如下:

int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Producer producer;Consumer consumerA;Consumer consumerB;producer.start();consumerA.start();consumerB.start();producer.wait();consumerA.wait();consumerB.wait();return a.exec();
}

程序最终的运行结果如图12.4所示。
在这里插入图片描述

12.3 多线程应用

12.3.1 【实例】:服务器编程

【例】(难度中等)(CH1204)服务器编程。
首先,建立服务器端工程“TimeServer.pro”。文件代码如下。
(1)在头文件“dialog.h”中,定义服务器端界面类Dialog继承自QDialog类,其具体代码如下:

#include <QDialog>
#include <QLabel>
#include <QPushButton>
class Dialog : public QDialog
{Q_OBJECT
public:Dialog(QWidget *parent = 0);~Dialog();
private:QLabel *Label1;				//此标签用于显示监听端口QLabel *Label2;				//此标签用于显示请求次数QPushButton *quitBtn;				//退出按钮
};

(2)在源文件“dialog.cpp”中,Dialog类的构造函数完成了初始化界面,其具体代码如下:

#include "dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
Dialog::Dialog(QWidget *parent): QDialog(parent)
{setWindowTitle(tr("多线程时间服务器"));Label1 =new QLabel(tr("服务器端口:"));Label2 = new QLabel;quitBtn = new QPushButton(tr("退出"));QHBoxLayout *BtnLayout = new QHBoxLayout;BtnLayout->addStretch(1);BtnLayout->addWidget(quitBtn);BtnLayout->addStretch(1);QVBoxLayout *mainLayout = new QVBoxLayout(this);mainLayout->addWidget(Label1);mainLayout->addWidget(Label2);mainLayout->addLayout(BtnLayout);connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));
}

(3)此时运行服务器端工程“TimeServer.pro”,界面显示如图12.5所示。
在这里插入图片描述

(4)在服务器端工程“TimeServer.pro”中,添加C++ Class文件“timethread.h”及“timethread.cpp”。在头文件“timethread.h”中,工作线程TimeThread类继承自QThread类,实现TCP套接字,其具体代码如下:

#include <QThread>
#include <QtNetwork>
#include <QTcpSocket>
class TimeThread : public QThread
{Q_OBJECT
public:TimeThread(int socketDescriptor,QObject *parent=0);void run();					//重写此虚函数
signals:void error(QTcpSocket::SocketError socketError);	//出错信号
private:int socketDescriptor;				//套接字描述符
};

(5)在源文件“timethread.cpp”中,TimeThread类的构造函数只是初始化了套接字描述符,其具体代码如下:

#include "timethread.h"
#include <QDateTime>
#include <QByteArray>
#include <QDataStream>
TimeThread::TimeThread(int socketDescriptor,QObject *parent):QThread(parent),socketDescriptor(socketDescriptor)
{
}

TimeThread::run()函数是工作线程(TimeThread)的实质所在,当在TimeServer:: incomingConnection()函数中调用了thread->start()函数后,此虚函数开始执行,其具体代码如下:

void TimeThread::run()
{QTcpSocket tcpSocket;			//创建一个QTcpSocket类if(!tcpSocket.setSocketDescriptor(socketDescriptor))	//(a){emit error(tcpSocket.error());			//(b)return;}QByteArray block;QDataStream out(&block,QIODevice::WriteOnly);out.setVersion(QDataStream::Qt_5_8);uint time2u = QDateTime::currentDateTime().toTime_t();//(c)out<<time2u;tcpSocket.write(block);			//将获得的当前时间传回客户端tcpSocket.disconnectFromHost();	//断开连接tcpSocket.waitForDisconnected();	//等待返回
}

(6)在服务器端工程“TimeServer.pro”中添加C++ Class文件“timeserver.h”及“timeserver.cpp”。在头文件“timeserver.h”中,实现了一个TCP服务器端,类TimeServer继承自QTcpServer类,其具体代码如下:

#include <QTcpServer>
class Dialog;                         			//服务器端的声明
class TimeServer : public QTcpServer
{Q_OBJECT
public:TimeServer(QObject *parent=0);
protected:void incomingConnection(int socketDescriptor);	//(a)
private:Dialog *dlg;					//(b)
};

(7)在源文件“timeserver.cpp”中,构造函数只是用传入的父类指针parent初始化私有变量dlg,其具体代码如下:

#include "timeserver.h"
#include "timethread.h"   
#include "dialog.h"
TimeServer::TimeServer(QObject *parent):QTcpServer(parent)
{dlg =(Dialog *)parent;
}

重写的虚函数incomingConnection()的具体代码如下:

void TimeServer::incomingConnection(int socketDescriptor)
{TimeThread *thread = new TimeThread(socketDescriptor,0);	//(a)connect(thread,SIGNAL(finished()),dlg,SLOT(slotShow()));	//(b)connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()),Qt::DirectConnection);				//(c)thread->start();					//(d)
}

(8)在服务器端界面的头文件“dialog.h”中添加的具体代码如下:

class TimeServer;
public slots:void slotShow();			//此槽函数用于界面上显示的请求次数
private:TimeServer *timeServer;		//TCP服务器端timeServerint count;				//请求次数计数器count

(9)在源文件“dialog.cpp”中,添加的头文件如下:

#include <QMessageBox>
#include "timeserver.h"

其中,在Dialog类的构造函数中添加的内容,用于启动服务器端的网络监听,其具体实现如下:

count=0;
timeServer = new TimeServer(this);
if(!timeServer->listen())
{QMessageBox::critical(this,tr("多线程时间服务器"),tr("无法启动服务器:%1.").arg(timeServer->errorString()));close();return;
}

Label1->setText(tr(“服务器端口:%1.”).arg(timeServer->serverPort()));
在源文件“dialog.cpp”中,槽函数slotShow()的具体内容如下:

void Dialog::slotShow()
{Label2->setText(tr("第%1次请求完毕。").arg(++count));
}

(10)在服务器端工程文件“TimeServer.pro”中添加如下代码:

QT += network

(11)最后运行服务器端工程“TimeServer.pro”,结果如图12.6所示。
在这里插入图片描述

12.3.2 【实例】:客户端编程

【例】(难度中等)(CH1205)客户端编程。界面效果如图12.7所示。
在这里插入图片描述

操作步骤如下。
(1)建立客户端工程“TimeClient.pro”。在头文件“timeclient.h”中,定义了客户端界面类TimeClient继承自QDialog类,其具体代码。
(2)在源文件“timeclient.cpp”中,TimeClient类的构造函数完成了初始化界面,其具体代码。
在源文件“timeclient.cpp”中,enableGetBtn()函数的具体代码如下:

void TimeClient::enableGetBtn()
{getBtn->setEnabled(!serverNameLineEdit->text().isEmpty()&&!portLineEdit->text().isEmpty());
}

在源文件“timeclient.cpp”中,getTime()函数的具体代码如下:

void TimeClient::getTime()
{getBtn->setEnabled(false);time2u =0;tcpSocket->abort();tcpSocket->connectToHost(serverNameLineEdit->text(),portLineEdit->text().toInt());
}

在源文件“timeclient.cpp”中,readTime ()函数的具体代码如下:

void TimeClient::readTime()
{QDataStream in(tcpSocket);in.setVersion(QDataStream::Qt_5_8);if(time2u==0){if(tcpSocket->bytesAvailable()<(int)sizeof(uint))return;in>>time2u;}dateTimeEdit->setDateTime(QDateTime::fromTime_t(time2u));getBtn->setEnabled(true);
}

在源文件“timeclient.cpp”中,showError()函数的具体代码如下:

void TimeClient::showError(QAbstractSocket::SocketError socketError)
{switch (socketError){case QAbstractSocket::RemoteHostClosedError:break;case QAbstractSocket::HostNotFoundError:QMessageBox::information(this, tr("时间服务客户端"),tr("主机不可达!"));break;case QAbstractSocket::ConnectionRefusedError:QMessageBox::information(this, tr("时间服务客户端"),tr("连接被拒绝!"));break;default:QMessageBox::information(this, tr("时间服务客户端"),tr("产生如下错误: %1.").arg(tcpSocket->errorString()));}getBtn->setEnabled(true);
}

(3)在客户端工程文件“TimeClient.pro”中,添加如下代码:

QT += network

(4)运行客户端工程“TimeClient.pro”,显示界面如图12.7所示。
最后,同时运行服务器和客户端程序,单击客户端“获取时间”按钮,从服务器上获得当前的系统时间,如图12.8所示。
在这里插入图片描述



本章相关例程源码下载

1.Qt5开发及实例_CH1201.rar 下载

Qt5开发及实例_CH1201.rar

2.Qt5开发及实例_CH1202.rar 下载

Qt5开发及实例_CH1202.rar

3.Qt5开发及实例_CH1203.rar 下载

Qt5开发及实例_CH1203.rar

4.Qt5开发及实例_CH1204.rar 下载

Qt5开发及实例_CH1204.rar

5.Qt5开发及实例_CH1205.rar 下载

Qt5开发及实例_CH1205.rar

相关文章:

Qt5开发及实例V2.0-第十二章-Qt多线程

Qt5开发及实例V2.0-第十二章-Qt多线程 第12章 Qt 5多线程12.1 多线程及简单实例12.2 多线程控制12.2.1 互斥量12.2.2 信号量12.2.3 线程等待与唤醒 12.3 多线程应用12.3.1 【实例】&#xff1a;服务器编程12.3.2 【实例】&#xff1a;客户端编程 本章相关例程源码下载1.Qt5开发…...

Windows 修改系统默认字体

Windows Registry Editor Version 5.00; 重装机后电脑屏幕及字体调整.reg.lnk ;; 显示器分辨率: 3840*2160 ;; 自定义缩放: 266 ;; 辅助功能 - 文本大小 - 110% ;; 最后 ClearType 文本调谐器; https://www.cnblogs.com/bolang100/p/8548040.html#WINDOWS 10 显示中的仅更改文…...

图像处理软件Photoshop 2024 mac新增功能

Photoshop 2024 mac是一款图像处理软件的最新版本。ps2024提供了丰富的功能和工具&#xff0c;使用户能够对照片、插图、图形等进行精确的编辑和设计。 Photoshop 2024 mac软件特点 快速性能&#xff1a;Photoshop 2024 提供了更快的渲染速度和更高效的处理能力&#xff0c;让用…...

JavaScript之观察者模式

本文作者为 360 奇舞团前端开发工程师 概述 在日常开发中&#xff0c;开发人员经常使用设计模式来解决软件设计中的问题。其中&#xff0c;观察者模式是一种常用的模式&#xff0c;它可以帮助开发人员更好地处理对象之间的通信。在 JavaScript 中&#xff0c;观察者模式的应用非…...

深入了解ln命令:创建硬链接和符号链接的实用指南

文章目录 1. 引言1.1 关于ln命令1.2 ln命令的作用和用途 2. 基本用法2.1 创建硬链接2.2 创建符号链接2.3 区别硬链接和符号链接 3. 操作示例3.1 创建硬链接的示例3.2 创建符号链接的示例3.3 查看链接信息 4. 注意事项和常见问题4.1 文件路径4.2 软链接的相对路径4.3 软链接的更…...

虚拟IP技术

1.说明 虚拟IP&#xff08;Virtual IP Address&#xff0c;简称VIP&#xff09;是一个未分配给真实弹性云服务器网卡的IP地址。 弹性云服务器除了拥有私有IP地址外&#xff0c;还可以拥有虚拟IP地址&#xff0c;用户可以通过其中任意一个IP&#xff08;私有IP/虚拟IP&#xf…...

蓝桥杯 题库 简单 每日十题 day5

01 字符计数 字符计数 题目描述 给定一个单词&#xff0c;请计算这个单词中有多少个元音字母&#xff0c;多少个辅音字母。 元音字母包括a,e&#xff0c;i,o&#xff0c;u&#xff0c;共五个&#xff0c;其他均为辅音字母。 输入描述 输入格式&#xff1a; 输入一行&#xff0…...

【计算机网络】图解路由器(一)

图解路由器&#xff08;一&#xff09; 1、什么是路由器&#xff1f;2、什么是路由选择&#xff1f;3、什么是转发&#xff1f;4、路由器设备有哪些类型&#xff1f;5、根据性能分类&#xff0c;路由器有哪些类型&#xff1f;5.1 高端路由器5.2 中端路由器5.3 低端路由器 6、什…...

C语言文件的相关操作

C语言中文件的相关操作 文件的打开 使用文件的打开函数需要引入这个头文件&#xff1a;#include <fcntl.h> open函数 int open(char const *pathname, int flags, mode_t mode) 功能&#xff1a;打开已有的文件或者创建新文件参数 pathname&#xff1a;文件路径名&…...

Java入门级简单定时任务TimerTask

如果要执行一些简单的定时器任务&#xff0c;无须做复杂的控制&#xff0c;也无须保存状态&#xff0c;那么可以考虑使用JDK 入门级的定期器Timer来执行重复任务。 一、原理 JDK中&#xff0c;定时器任务的执行需要两个基本的类&#xff1a; java.util.Timer; java…...

Linux命令行教程:使用head和tail命令快速查看文件的开头和结尾

文章目录 简介A. 什么是head和tail命令B. head和tail命令的作用和用途 head命令A. 命令格式和语法B. 常见选项和参数1. -n&#xff1a;指定显示的行数2. -c&#xff1a;指定显示的字节数3. -v&#xff1a;显示文件名 C. 示例和应用实例1. 显示文件的前几行2. 显示多个文件的前几…...

[CISCN 2019 初赛]Love Math 通过进制转换执行命令

目录 hex2bin bin2hex base_convert 动态函数 第一种解法 通过get获取参数 绕过 第二种解法 读取请求头 getallheaders echo a,b 第三种解法 异或获得更多字符 这道题也是很有意思&#xff01; 通过规定白名单和黑名单 指定了 函数为数学函数 并且参数也只能是规…...

【Linux】系统编程生产者消费者模型(C++)

目录 【1】生产消费模型 【1.1】为何要使用生产者消费者模型 【1.2】生产者消费者模型优点 【2】基于阻塞队列的生产消费者模型 【2.1】生产消费模型打印模型 【2.2】生产消费模型计算公式模型 【2.3】生产消费模型计算公式加保存任务模型 【2.3】生产消费模型多生产多…...

【数据结构】图的应用:最小生成树;最短路径;有向无环图描述表达式;拓扑排序;逆拓扑排序;关键路径

目录 1、最小生成树 1.1 概念 1.2 普利姆算法&#xff08;Prim&#xff09; 1.3 克鲁斯卡尔算法&#xff08;Kruskal&#xff09; 2、最短路径 2.1 迪杰斯特拉算法&#xff08;Dijkstra&#xff09; 2.2 弗洛伊德算法&#xff08;Floyd&#xff09; 2.3 BFS算法&…...

大数据驱动业务增长:数据分析和洞察力的新纪元

文章目录 大数据的崛起大数据的特点大数据技术 大数据驱动业务增长1. 洞察力和决策支持2. 个性化营销3. 风险管理4. 产品创新 大数据分析的新纪元1. 云计算和大数据示例代码&#xff1a;使用AWS的Elastic MapReduce&#xff08;EMR&#xff09;进行大数据分析。 2. 人工智能和机…...

科技云报道:分布式存储红海中,看天翼云HBlock如何突围?

科技云报道原创。 过去十年&#xff0c;随着技术的颠覆性创新和新应用场景的大量涌现&#xff0c;企业IT架构出现了稳态和敏态的混合化趋势。 在持续产生海量数据的同时&#xff0c;这些新应用、新场景在基础设施层也普遍基于敏态的分布式架构构建&#xff0c;从而对存储技术…...

Java高级-动态代理

动态代理 1.介绍2.案例 1.介绍 public interface Star {String sing(String name);void dance(); }public class BigStar implements Star{private String name;public BigStar(String name) {this.name name;}public String sing(String name) {System.out.println(this.name…...

时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短…...

n个不同元素进栈,求出栈元素的【不同排列】以及【排列的数量】?

我在网上看的博客大部分是告诉你这是卡特兰数&#xff0c;然后只给出了如何求解有多少种排列&#xff0c;没有给出具体排列是怎么样的。如果你还不知道卡特兰数&#xff0c;请查看&#xff1a;https://leetcode.cn/circle/discuss/lWYCzv/ 这里记录一下如何生成每种具体的排列…...

Python中TensorFlow的长短期记忆神经网络(LSTM)、指数移动平均法预测股票市场和可视化...

原文链接&#xff1a;http://tecdat.cn/?p23689 本文探索Python中的长短期记忆&#xff08;LSTM&#xff09;网络&#xff0c;以及如何使用它们来进行股市预测&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 在本文中&#xff0c;你将看到如何使用…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...