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

C++客户端Qt开发——多线程编程(一)

多线程编程(一)

①QThread

在Qt中,多线程的处理一般是通过QThread类来实现。

QThread代表一个在应用程序中可以独立控制的线程,也可以和进程中的其他线程共享数据。

QThread对象管理程序中的一个控制线程。

run()

线程的入口函数

start()

通过调用run()开始执行函数,操作系统将根据优先级参数调度线程,如果线程已经在运行,这个函数什么也不做

currentThread()

返回一个指向管理当前执行线程的QThread的指针

isRunning()

如果线程正在运行则返回true,否则返回false

sleep() / msleep() / usleep()

使线程休眠,单位为秒/毫秒/微秒

wait()

阻塞线程,直到满足以下任何一个条件:

  • 与此QThread对象关联的线程已经完成执行(即当它从run(0返回时)。如果线程已经完成,这个函数将返回true。如果线程尚未启动,它也返回true。
  • 已经过了几毫秒。如果时间是ULONG MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。如果等待超时,此函数将返回false。
  • 这提供了与POSIX pthread_join()函数类似的功能。

terminate()

终止线程的执行。线程可以立即终止,也可以不立即终止,这取决于操作系统的调度策略。在terminate()之后使用QThread:wait()来确保。

finished()

当线程结束时会发出该信号,可以通过该信号来实现线程的清理工作。

②两种多线程使用方式

1>第一种

Qt中提供的多线程的第一种使用方式的特点是:简单

  1. 需要创建一个线程类的子类,让其继承QT中的线程类QThread,例如:
class MyThread:public QThread
{......
}
  1. 重写父类中的run()方法,在该函数内部编写子线层要处理的具体的业务流程
class MyThread:public QThread
{......protected:void run(){........}
}
  1. 在主线程中new一个子线程对象,在主线程中合适的位置启动子线程,调用start()方法
MyThread * subThread = new MyThread;subThread->start();

不能在类的外部调用run() 方法启动子线程,在外部调用start()相当于让run()开始运行

当子线程别创建出来之后,父子线程之间的通信可以通过信号槽的方式,注意事项:

  • 在Qt中在子线程中不要操作程序中的窗口类型对象, 不允许, 如果操作了程序就挂了
  • 只有主线程才能操作程序中的窗口对象, 默认的线程就是主线程, 自己创建的就是子线程

这种在程序中添加子线程的方式是非常简单的,但是也有弊端,假设要在一个子线程中处理多个任务,所有的处理逻辑都需要写到run()函数中,这样该函数中的处理逻辑就会变得非常混乱,不太容易维护。

示例:生成随机数,然后使用冒泡排序和快速排序对其进行处理

创建类的方法就不多说了

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QThread>
#include<QVector>//生成随机数,将构造类的名字直接改成Generate更明确一些
class Generate : public QThread
{Q_OBJECT
public:explicit Generate(QObject *parent = nullptr);//将主线程传递过来的数保存到m_numvoid recvNum(int num);protected:void run() override;signals:void sendArray(QVector<int>);private:int m_num;};class BubbleSort : public QThread
{Q_OBJECT
public:explicit BubbleSort(QObject *parent = nullptr);//将主线程传递过来的数保存到m_numvoid recvArray(QVector<int> list);protected:void run() override;signals:void finish(QVector<int>);private:QVector<int> m_list;};class QuickSort : public QThread
{Q_OBJECT
public:explicit QuickSort(QObject *parent = nullptr);//将主线程传递过来的数保存到m_numvoid recvArray(QVector<int> list);protected:void run() override;private:void quickSort(QVector<int> &list,int l, int r);signals:void finish(QVector<int>);private:QVector<int> m_list;};#endif // MYTHREAD_H

 mythread.cpp

#include "mythread.h"
#include<QElapsedTimer>
#include<QDebug>Generate::Generate(QObject *parent) : QThread(parent)
{}void Generate::recvNum(int num)
{m_num = num;
}void Generate::run()
{qDebug() << "生成随机数的线程的线程地址:" << QThread::currentThread();QVector<int> list;//计时QElapsedTimer time;time.start();for(int i=0; i<m_num; ++i){list.push_back(qrand() % 100000);}int milsec = time.elapsed();qDebug() << "生成" << m_num << "随机数总用时:" << milsec << "毫秒";//发送给主线程emit sendArray(list);
}BubbleSort::BubbleSort(QObject *parent):QThread(parent)
{}void BubbleSort::recvArray(QVector<int> list)
{m_list = list;
}void BubbleSort::run()
{qDebug() << "冒泡排序的线程的线程地址:" << QThread::currentThread();//计时QElapsedTimer time;time.start();//冒泡排序int temp;for(int i=0;i<m_list.size();++i){for(int j=0;j<m_list.size()-i-1;++j){if(m_list[j] > m_list[j+1]){temp = m_list[j];m_list[j] = m_list[j+1];m_list[j+1] = temp;}}}int milsec = time.elapsed();qDebug() << "冒泡排序用时:" << milsec << "毫秒";emit finish(m_list);
}QuickSort::QuickSort(QObject *parent):QThread(parent)
{}void QuickSort::recvArray(QVector<int> list)
{m_list = list;
}void QuickSort::run()
{qDebug() << "快速排序的线程的线程地址:" << QThread::currentThread();//计时QElapsedTimer time;time.start();//快速排序quickSort(m_list,0,m_list.size()-1);int milsec = time.elapsed();qDebug() << "快速排序用时:" << milsec << "毫秒";emit finish(m_list);
}void QuickSort::quickSort(QVector<int> &s, int l, int r)
{if(l<r){int i = l,j = r;int x = s[l];while(i < j){while(i < j && s[j] >= x){j--;}if(i < j){s[i++] = s[j];}while(i < j && s[i] < x){i++;}if(i < j){s[j--] = s[i];}}s[i] = x;quickSort(s,l,i-1);quickSort(s,i+1,r);}
}

 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void starting(int num);private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp 

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<mythread.h>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//1.创建子线程对象Generate* gen = new Generate;BubbleSort* bubble = new BubbleSort;QuickSort* quick = new QuickSort;connect(this,&MainWindow::starting,gen,&Generate::recvNum);//2.启动子线程connect(ui->start,&QPushButton::clicked,this,[=](){emit starting(10000);gen->start();});//随机数子线程发送来的数据触发冒泡排序和快速排序接收数据connect(gen,&Generate::sendArray,bubble,&BubbleSort::recvArray);connect(gen,&Generate::sendArray,quick,&QuickSort::recvArray);//接收子线程发送的数据,显示在randlist里,同时启动两个排序子线程connect(gen,&Generate::sendArray,this,[=](QVector<int> list){//启动两个子线程方法bubble->start();quick->start();for (int i=0; i<list.size(); ++i) {ui->randlist->addItem(QString::number(list.at(i)));}});//两个排序子线程处理数据connect(bubble,&BubbleSort::finish,this,[=](QVector<int> list){for (int i=0; i<list.size(); ++i) {ui->bubblelist->addItem(QString::number(list.at(i)));}});connect(quick,&QuickSort::finish,this,[=](QVector<int> list){for (int i=0; i<list.size(); ++i) {ui->quicklist->addItem(QString::number(list.at(i)));}});//资源释放connect(this,&MainWindow::destroyed,this,[=](){//线程释放gen->quit();gen->wait();gen->deleteLater();bubble->quit();bubble->wait();bubble->deleteLater();quick->quit();quick->wait();quick->deleteLater();});}MainWindow::~MainWindow()
{delete ui;
}

main.cpp 

#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);//向 Qt 元对象系统注册 QVector<int> 类型qRegisterMetaType<QVector<int>>("QVector<int>");MainWindow w;w.show();return a.exec();
}

为什么需要注册类型?

在 Qt 中,某些类型在信号和槽机制中传递时需要被注册。Qt 默认支持一些基本类型(如 intdoubleQString 等),但是对于自定义类型或者某些复杂类型(如 QVector<int>),需要显式注册。

  • 信号和槽机制: Qt 的信号和槽机制允许对象之间进行通信。当一个信号发出时,连接到该信号的槽函数会被调用。如果你希望信号或槽函数使用 QVector<int> 这样的类型,你需要向 Qt 注册该类型,这样 Qt 才能在运行时识别和处理这种类型。
  • QMetaType 系统: Qt 的元对象系统(QMetaType)需要知道所有使用的类型,以便能够在运行时进行类型转换、创建对象和处理信号槽连接等操作。通过 qRegisterMetaType 注册类型,可以确保 Qt 的元对象系统能够识别并处理这种类型。

2>第二种

Qt提供的第二种线程的创建方式弥补了第一种方式的缺点,用起来更加灵活,但是这种方式写起来会相对复杂一些

  1. 创建一个新的类,让其从QObject派生(QObject类提供moveToThread()方法)
class MyWork:public QObject
{.......
}
  1. 在这个类中添加一个公共的成员函数,函数体就是我们要子线程中执行的业务逻辑
class MyWork:public QObject
{
public:.......// 函数名自己指定, 叫什么都可以, 参数可以根据实际需求添加void working();
}
  1. 在主线程中创建一个QThread对象,这就是子线程的对象
QThread* sub = new QThread;
  1. 在主线程中创建工作的类对象
MyWork* work = new MyWork(this);    // 错误,这里不能指定this
//如果指定了父类为this,那就无法移动任务到其他的线程中
MyWork* work = new MyWork;          // 正确,什么也不用指定
  1. 将MyWork对象移动到创建的子线程对象中,需要调用QObject类提供的moveToThread()方法
// void QObject::moveToThread(QThread *targetThread);
// 如果给work指定了父对象, 这个函数调用就失败了,也就是第四步中提到的this
// 提示: QObject::moveToThread: Cannot move objects with a parent
work->moveToThread(sub);	// 移动到子线程中工作
  1. 启动子线程,调用start(),这时候线程启动了,但是移动到线程中的对象并没有工作
  2. 调用MyWork类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的
示例:生成随机数,然后使用冒泡排序和快速排序对其进行处理

直接复制了上边示例的项目,所以这里的类名还是一致的,但是这种方法是从QObject中派生的,命名在这里用于区分代码就好

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QObject>
#include<QVector>//生成随机数,将构造类的名字直接改成Generate更明确一些
class Generate : public QObject
{Q_OBJECT
public:explicit Generate(QObject *parent = nullptr);void working(int num);signals:void sendArray(QVector<int>);};class BubbleSort : public QObject
{Q_OBJECT
public:explicit BubbleSort(QObject *parent = nullptr);void working(QVector<int> list);signals:void finish(QVector<int>);};class QuickSort : public QObject
{Q_OBJECT
public:explicit QuickSort(QObject *parent = nullptr);void working(QVector<int> list);private:void quickSort(QVector<int> &list,int l, int r);signals:void finish(QVector<int>);};#endif // MYTHREAD_H

 mythread.cpp

#include "mythread.h"
#include<QElapsedTimer>
#include<QDebug>
#include<QThread>Generate::Generate(QObject *parent) : QObject(parent)
{}void Generate::working(int num)
{qDebug() << "生成随机数的线程的线程地址:" << QThread::currentThread();QVector<int> list;//计时QElapsedTimer time;time.start();for(int i=0; i<num; ++i){list.push_back(qrand() % 100000);}int milsec = time.elapsed();qDebug() << "生成" << num << "随机数总用时:" << milsec << "毫秒";//发送给主线程emit sendArray(list);
}BubbleSort::BubbleSort(QObject *parent):QObject(parent)
{}void BubbleSort::working(QVector<int> list)
{qDebug() << "冒泡排序的线程的线程地址:" << QThread::currentThread();//计时QElapsedTimer time;time.start();//冒泡排序int temp;for(int i=0;i<list.size();++i){for(int j=0;j<list.size()-i-1;++j){if(list[j] > list[j+1]){temp = list[j];list[j] = list[j+1];list[j+1] = temp;}}}int milsec = time.elapsed();qDebug() << "冒泡排序用时:" << milsec << "毫秒";emit finish(list);
}QuickSort::QuickSort(QObject *parent):QObject(parent)
{}void QuickSort::working(QVector<int> list)
{qDebug() << "快速排序的线程的线程地址:" << QThread::currentThread();//计时QElapsedTimer time;time.start();//快速排序quickSort(list,0,list.size()-1);int milsec = time.elapsed();qDebug() << "快速排序用时:" << milsec << "毫秒";emit finish(list);
}void QuickSort::quickSort(QVector<int> &s, int l, int r)
{if(l<r){int i = l,j = r;int x = s[l];while(i < j){while(i < j && s[j] >= x){j--;}if(i < j){s[i++] = s[j];}while(i < j && s[i] < x){i++;}if(i < j){s[j--] = s[i];}}s[i] = x;quickSort(s,l,i-1);quickSort(s,i+1,r);}
}

 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void starting(int num);private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<mythread.h>
#include<QThread>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//1.创建子线程对象QThread* t1 = new QThread;QThread* t2 = new QThread;QThread* t3 = new QThread;//2.创建任务类的对象Generate* gen = new Generate;BubbleSort* bubble = new BubbleSort;QuickSort* quick = new QuickSort;//3.将任务对象移动到某个子线程中gen->moveToThread(t1);bubble->moveToThread(t2);quick->moveToThread(t3);connect(this,&MainWindow::starting,gen,&Generate::working);//2.启动子线程connect(ui->start,&QPushButton::clicked,this,[=](){emit starting(10000);t1->start();});//随机数子线程发送来的数据触发冒泡排序和快速排序接收数据connect(gen,&Generate::sendArray,bubble,&BubbleSort::working);connect(gen,&Generate::sendArray,quick,&QuickSort::working);//接收子线程发送的数据,显示在randlist里,同时启动两个排序子线程connect(gen,&Generate::sendArray,this,[=](QVector<int> list){//启动两个子线程方法t2->start();t3->start();for (int i=0; i<list.size(); ++i) {ui->randlist->addItem(QString::number(list.at(i)));}});//两个排序子线程处理数据connect(bubble,&BubbleSort::finish,this,[=](QVector<int> list){for (int i=0; i<list.size(); ++i) {ui->bubblelist->addItem(QString::number(list.at(i)));}});connect(quick,&QuickSort::finish,this,[=](QVector<int> list){for (int i=0; i<list.size(); ++i) {ui->quicklist->addItem(QString::number(list.at(i)));}});//资源释放connect(this,&MainWindow::destroyed,this,[=](){//线程释放t1->quit();t1->wait();t1->deleteLater();t2->quit();t2->wait();t2->deleteLater();t3->quit();t3->wait();t3->deleteLater();//任务对象释放gen->deleteLater();bubble->deleteLater();quick->deleteLater();});}MainWindow::~MainWindow()
{delete ui;
}

 main.cpp

#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);qRegisterMetaType<QVector<int>>("QVector<int>");MainWindow w;w.show();return a.exec();
}

connect函数第五个参数

connect()函数第五个参数为Qt:ConnectionType,用于指定信号和槽的连接类型。同时影响信号的传递方式和槽函数的执行顺序。只有在多线程的时候才意义。

Qt:ConnectionType提供了以下五种方式:

Qt:AutoConnection

在Qt中,会根据信号和槽函数所在的线程自动选择连接类型。如果信号和槽函数在同一线程中,那么使用Qt:DirectConnection类型;如果它们位于不同的线程中,那么使用Qt:QueuedConnection类型。

Qt:DirectConnection

当信号发出时,槽函数会立即在同一线程中执行。这种连接类型适用于信号和槽函数在同一线程中的情况,可以实现直接的函数调用,但需要注意线程安全性

Qt::QueuedConnection

当信号发出时,槽函数会被插入到接收对象所属的线程的事件队列中,等待下一次事件循环时执行。这种连接类型适用于信号和槽函数在不同线程中的情况,可以确保线程安全

Qt:BlockingQueuedConnection

Qt:QueuedConnection类似,但是发送信号的线程会被阻塞,直到槽函数执行完毕,这种连接类型适用于需要等待槽函数执行完毕再继续的场景,但需要注意可能引起线程死锁的风险

Qt:UniqueConnection

这是一个标志,可以使用位或与上述任何一种连接类型组合使用。

相关文章:

C++客户端Qt开发——多线程编程(一)

多线程编程&#xff08;一&#xff09; ①QThread 在Qt中&#xff0c;多线程的处理一般是通过QThread类来实现。 QThread代表一个在应用程序中可以独立控制的线程&#xff0c;也可以和进程中的其他线程共享数据。 QThread对象管理程序中的一个控制线程。 run() 线程的入口…...

安装pnpm

安装pnpm&#xff08;Performant npm&#xff09;&#xff0c;即高性能的npm包管理工具&#xff0c;可以通过多种方式进行。以下是详细的安装步骤&#xff1a; 一、通过npm全局安装 打开命令行工具&#xff1a;在你的计算机上打开命令行工具&#xff0c;例如Windows的CMD、Pow…...

CSS平移实现双开门效果

CSS平移实现双开门效果 一共要三张图片&#xff0c;一张作为父级背景&#xff0c;两张为兄弟左右布局 父子结构布局 一张作为父级背景&#xff0c;两张为兄弟左右布局。之后添加鼠标悬停效果&#xff0c;两张子图分别从左右平移 [外链图片转存失败,源站可能有防盗链机制,建议…...

3096. 得到更多分数的最少关卡数目

3096. 得到更多分数的最少关卡数目 题目链接&#xff1a;3096. 得到更多分数的最少关卡数目 代码如下&#xff1a; class Solution { public:int minimumLevels(vector<int>& possible) {int s0;//两个玩家能得到的分数和for(int x:possible){sx0?-1:1;}int t0;/…...

AGI思考探究的意义、价值与乐趣Ⅳ

探究in context或Prompt对于LLM来说其根本意义&#xff0c;in context & Prompt Learning带给我们更深一层的提示是什么&#xff1f; 文章里的探索希望能够将in context或Prompt置身于一个更全局的视角来看待&#xff1a;将其视为在真实世界中时空认知流形所映射为数据流形…...

《数据结构》(C语言版)第1章 绪论(上)

第1章 绪论 1.1 数据结构的研究内容1.2 基本概念和术语 1.1 数据结构的研究内容 N.沃思&#xff08;Niklaus Wirth)教授提出&#xff1a; 程序算法数据结构 电子计算机的主要用途 早期&#xff1a;主要用于数值计算 后来&#xff1a;非数值计算&#xff0c;复杂的具有一定结构…...

【Pyhton】数据类型之详讲字符串(上)

本篇文章将详细讲解字符串&#xff1a; 1、定义 定义字符串时&#xff0c;字符串的内容被双引号&#xff0c;单引号&#xff0c;三单引号&#xff0c;三双引号中的其中一个被括住。 例如&#xff1a; 双引号&#xff1a; v1"haha" 单引号&#xff1a; v1hahah…...

算法小白的进阶之路(力扣6~8)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...

【期货】收盘点评。昨天说的,p2409棕榈油在今天或者周一会走出行情

收盘点评 昨天说的&#xff0c;p2409棕榈油在今天或者周一会走出行情。事实就是如此。震荡了几天了&#xff0c;波幅不大的来回震荡&#xff0c;其实主力是不想震荡的&#xff0c;但是不震荡自己的货和行情走不出来。所以我昨天就说&#xff0c;应该就是这一两天会走出一波小行…...

LBS 开发微课堂|Polyline绘制优化:效果更丰富,性能更佳!

为了让广大的开发者 更深入地了解 百度地图开放平台的技术能力 轻松掌握满满的技术干货 更加简单地接入 开放平台的服务 我们特别推出了 “位置服务&#xff08;LBS&#xff09;开发微课堂” 系列技术案例 第一期的主题是 《Polyline 绘制优化升级》 你还想了解哪些…...

VS Code设置C++编译器路径

C_Cpp.default.compilerPath是C/C编译器路径; python.condaPath是conda路径....

laravel项目配置

创建laravel项目 composer create-project --prefer-dist laravel/laravel 项目名称生成项目key php artisan key:generate.清理配置缓存 php artisan config:clearlaravel生成代码 官网链接 php artisan make:model Flight --all生成Flight类相关的文件&#xff0c;对应数…...

Python试讲

Python试讲 导语Python简介Python及其特点如何使用Python Python与计算计算变量 导语 本次试讲内容如下&#xff1a;Python简介与使用&#xff0c;Python与基本运算 辅助教材为 《趣学Python编程》和《Python编程从入门到实践》 Python简介 Python是目前入门最简单最好学的…...

RESTful API

RESTful API是一种基于REST (Representational State Transfer) 架构风格的应用程序编程接口。它通过使用HTTP协议的不同方法&#xff08;如GET、POST、PUT、DELETE等&#xff09;来对资源进行操作和传输数据。 使用RESTful API构建web应用程序需要遵循以下几个步骤&#xff1…...

NEEP-EN2-2020-Text1

英二-2020-Text 1 摘自新科学家&#xff08;New scientist&#xff09;2018年11月的文章《Rats can make friends with robot rats and will rescue them when stuck》。 以下为个人解析&#xff0c;非官方公开标准资料&#xff0c;可能有误&#xff0c;仅供参考。&#xff08;…...

摩托罗拉E6系统研究

这是很久以前研究摩托罗拉E6刷机包时总结的一些经验&#xff0c;不一定准确但留个纪念&#xff0c;希望会制作刷机包的高手交流学习。 ------------------------------------------------------------------------------------------------------------------------------- 摩…...

Spring中,ApplicationContext主要的实现类型包括?

Spring中&#xff0c;‌ApplicationContext主要的实现类型包括FileSystemXmlApplicationContext、‌ClassPathXmlApplicationContext、‌XmlWebApplicationContext、‌AnnotationConfigWebApplicationContext。‌ FileSystemXmlApplicationContext&#xff1a;‌这个实现从一个…...

JavaScript青少年简明教程:事件及处理

JavaScript青少年简明教程&#xff1a;事件及处理 在编程语言中&#xff0c;事件&#xff08;Event&#xff09;是一种使程序能够响应特定操作或条件发生的机制。它允许程序中的不同部分&#xff08;比如对象、类或模块&#xff09;在发生某些特定情况时互相通信或协作。事件驱…...

node_exporter

目录 指标详解常用指标 指标详解 指标描述node_arp_entriesARP&#xff08;Address Resolution Protocol&#xff09;表中的条目数量&#xff0c;用于将IP地址映射到MAC地址。node_boot_time_seconds系统启动时间的Unix时间戳&#xff0c;表示从1970年1月1日以来的秒数。node…...

近期在看

1. C Primer 2. 深入理解 FFmpeg 3. 鸿蒙 sdk 开发...

C++篇:C++入门基础(1)

C前言&#xff1a; C 的发展历史可以追溯到1979年&#xff0c;当时C语言以其效率和灵活性成为广泛使用的系统编程语言&#xff0c;但它也有一些限制&#xff0c;例如缺乏直接支持面向对象编程&#xff08;OOP&#xff09;的特性。 之后Bjarne Stroustrup(也就是C之父)是C的创始…...

【Linux】网络编程_3

文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础 5. socket编程 socket 常见API // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服…...

Kafka设计与原理详解

RocketMQ 是一款开源的分布式消息系统&#xff0c;基于高可用分布式集群技术&#xff0c;提供低延时的、高可靠的消息发布与订阅服务。同时&#xff0c;广泛应用于多个领域&#xff0c;包括异步通信解耦、企业解决方案、金融支付、电信、电子商务、快递物流、广告营销、社交、即…...

IPV6公网暴露下的OPENWRT防火墙安全设置(只允许访问局域网中指定服务器指定端口其余拒绝)

首先是防火墙的常规配置和区域配置 标的有点乱但是选项含义都做了解释&#xff0c;看不懂可以直接按图抄作业。 其次是对需要访问的端口做访问放通 情况1 DDNS位于openwrt网关上&#xff0c;外网访问openwrt&#xff0c;通过端口转发访问内部服务器。此情况需要设置端口转发。 …...

单调栈② | Java | LeetCode 接雨水 最大的矩形

42. 接雨水 暴力法 for循环遍历每一个柱子&#xff0c;内层for循环找到左边和右边比它高的柱子 时间复杂度 n^2 优化&#xff1a;添加一个预处理 定义一个数组&#xff0c;存放该柱子右边比他高的柱子是哪一个 再用一个数组&#xff0c;存放该柱子左边比他高的柱子是哪一个 …...

2024年全国青少年信息素养大赛总决赛日赛程表

2024全国青少年信息素养大赛赛程表分赛场&#xff08;浙江传媒学院桐乡校区、桐乡技师学院&#xff09;日期地点时间赛项16日传媒学院8:00-9:00检录 9:00-10:30开赛图形化编程挑战赛&#xff08;小学1-3年级&#xff09;A组12:00-13:00检录 13:00-14:30开赛图形化编程挑战赛&am…...

PHP:连接钉钉接口-钉钉回调事件,本地测试数据

前置数据参考 数据说明&#xff1a;参见官方文档回调事件消息体加解密 - 钉钉开放平台 (dingtalk.com) URL后面带的参数&#xff1a; signature5a65ceeef9aab2d149439f82dc191dd6c5cbe2c0&timestamp1445827045067&noncenEXhMP4r Post参数&#xff1a; { &quo…...

【C++标准模版库】vector的介绍及使用

vector 一.vector的介绍二.vector的使用1.vector 构造函数2.vector 空间增长3.vector 增删查改4.vector 迭代器的使用1.正向迭代器2.反向迭代器 5.victor 迭代器失效问题&#xff08;重点&#xff09; 三.vector不支持 流提取与流插入四.vector存储自定义类型1.存储string2.存储…...

数说故事|引爆社媒的森贝儿IP,品牌如何实现流量变现?

以可爱、雅痞、贱萌......的外表加魔性舞姿出圈的可爱小狗——森贝儿贵宾犬Milo&#xff0c;用“可爱微怒”的表情演绎着当代打工人的“疯态”&#xff0c;并迅速晋升成不少打工人高频使用的表情包。 最近几年&#xff0c;“萌系”爆款IP频出&#xff0c;用小动物的形象、可爱…...

使用openpyxl库对Excel条件格式的深度探索

哈喽,大家好,我是木头左! openpyxl中的条件格式 在openpyxl中,可以使用ConditionalFormatting类来创建和管理条件格式。这个类有两个主要的方法:add_conditional_formatting()和remove_conditional_formatting(),分别用于添加和删除条件格式。 add_conditional_formatt…...

天猫网站怎么做/宁波seo网站推广

Offer_day03_58 - II. 左旋转字符串 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如&#xff0c;输入字符串"abcdefg"和数字2&#xff0c;该函数将返回左旋转两位得到的结果"cdefgab"…...

设计公司给公司做网站用了方正字体/深圳网站建设微信开发

1. MVC设计模式简介 MVC&#xff1a;Model-View-Controller&#xff0c;模型-视图-控制器&#xff0c;MVC是一种软件开发架构模式。 1.1 MVC设计模式结构 MVC设计模式三个基本组成部分之间关系如下图所示&#xff1a; 模型&#xff08;Model&#xff09;&#xff1a;负责对整个…...

wordpress微信对接/seo如何快速排名百度首页

准备材料 高筋面粉&#xff0c;分量自己把握&#xff08;据说高筋和低筋混合的比较好&#xff09;酵母粉&#xff0c;分量自己把握黄油&#xff0c;分量自己把握花生油&#xff08;有条件的可以用橄榄油&#xff09;盐糖酱油料酒番茄酱洋葱彩椒翅根芝士&#xff08;非常重要&a…...

网站首页布局分析/app推广引流渠道

算是读书笔记吧二进制编码二进制和我们平时用的十进制&#xff0c;其实并没有什么本质区别&#xff0c;只是平时我们是“逢十进一”&#xff0c;这里变成了“逢二进一”而已。每一位&#xff0c;相比于十进制下的 0&#xff5e;9 这十个数字&#xff0c;我们只能用 0 和 1 这两…...

衡阳网站建设衡阳千度网络/友链对网站seo有帮助吗

“微软实现企业信息系统远程客户端的安全技术及应用-石家庄站活动”召开在即&#xff01;热忱期待您的光临&#xff01;感谢您对本次活动的热情参与&#xff01; 城 市&#xff1a;石家庄 时 间&#xff1a;2005年11月16日 13&#xff1a;30-17&#xff1a…...

临沂天元建设集团/宁波seo营销

创建文档时&#xff0c;将图片放入单元格&#xff0c;随着单元格宽与高的改变&#xff0c;图片大小也跟着变化 导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.4</version>&…...