当前位置: 首页 > 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;你将看到如何使用…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...