Qt的多线程编程

2023-05-16

Qt线程

基本概念

并发

当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态。虽然看起来所有线程都是一起执行的,但是其实每个时间只有一个线程在执行,这种方式我们成为并发。

并行

当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行。因此,多核CPU可以同时执行多个进程。

为什么需要使用线程

例:给定需求计算一个复杂数据处理所花费的时间。我们有一个start按钮,和一个LCD显示时间,点击按钮开始计时并进行数据处理。

我们创建一个定时器。在start的槽函数中启动定时器,并进行数据处理,数据处理利用QThread::sleep(5)来模拟。定时器每次触发timeout信号,更新LCD数字。代码如下:

//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTimer>
#include <QThread>

//利用定时器计算复杂计算花费的时间
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTimer *_timer = new QTimer(this);
};

#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget) {
    ui->setupUi(this);

    //只要定时器启动,自动触发timeout信号,每次触发timeout信号更新LCD显示
    connect(_timer, &QTimer::timeout, [=]() {
       static int i = 0;
       i++;
       ui->lcdNumber->display(i);
    });
}

Widget::~Widget() {
    delete ui;
}

void Widget::on_pushButton_clicked() {
    //如果定时器没有工作才启动
    if (_timer->isActive() == false) {
        _timer->start(100);
    }

    //非常复杂的数据处理,需要花费5s
    QThread::sleep(5);
}

//widget.ui

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ya1AvSrx-1662045421466)(C:\Users\84995\AppData\Roaming\Typora\typora-user-images\1662018717956.png)]

运行结果发现,当数据处理结束5秒后,才开始计时。这样我们就不能计算数据处理所花费的时间了。这是为什么呢?

因为这是个单线程的程序,定时器计时和数据处理不是并行处理,而是数据处理结束之后才开始定时器计时。而如果我们使用多线程,则这两个任务可以同时执行,我们就可以得到数据处理所耗费的时间了。

使用多线程的好处

  • 使用多线程可以把占据长时间的任务放到后台去处理
  • 用户界面可以更加吸引人,例如用户点击了一个按键去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
  • 程序的运行可能加快。
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等了。

什么时候该使用多线程

大多情况下,要用到多线程的主要是需要处理大量的IO操作时或处理的情况需要花费大量的时间等等。例如:

  • 读写文件
  • 视屏图像的采集、处理、显示、保存等

Qt4实现多线程的方法

Qt4实现多线程的方法

1.自定义一个线程类,继承于QThread

class MyThread : public QThread {
public:
    void run(); //线程处理函数(和主线程不在同一个线程,可以同时处理)

signals:
    void isDone(); //线程处理数据结束发送这个信号
}

2.主线程中启动子线程,开始并行处理。注意,主线程不能直接调用子线程的run函数,而需要通过子线程类的对象调用start()函数间接低矮用run()函数。

//主线程类中
MyThread *_thread = new MyThread(this);
_thread->start(); //通过子线程对象调用start间接调用子线程的run()函数

子线程的创建

还是以刚才的背景为例,我们准备求得复杂信号处理的时长。我们县创建一个子线程类MyThread.

注意以下几点:

  • void run()函数是protected继承的QThread的虚函数,它是线程处理函数实现的入口
  • run()函数不能直接调用,必须通过MyThread类实例化对象调用start()间接调用。
//MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

signals:
    void isDone();

protected:
    //QThread的虚函数
    //线程处理函数
    //不能直接调用,通过start()间接调用
    void run(); //线程处理实现入口

public slots:
};

#endif // MYTHREAD_H
//MyThread.cpp
#include "mythread.h"

MyThread::MyThread(QObject *parent) : QThread(parent) {

}

void MyThread::run() {
    //复杂的数据处理,用sleep来模拟,大概耗费5s
    sleep(5);

    //处理结束,发送处理完成信号
    emit isDone();
}

主线程中调用子线程

注意:

  • 线程号是有限的,空闲的线程也会占用资源,我们使用完线程后也要将线程关闭。
  • 线程关闭可以用quit()函数来关闭,quit()会等待线程执行完当前工作再关闭线程
  • 关闭完线程之后,需要回收线程资源,**通过wait()来回收线程资源。**类似于Linux回收进程资源的wait和waitpid。
//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTimer>
#include <QThread>
#include <mythread.h>

//需求,点击start按钮,显示定时器时间
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

public slots:
    void DealIsDone();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTimer *_timer = new QTimer(this);
    MyThread *_thread = new MyThread(this);
};

#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget) {
    ui->setupUi(this);

    //只要定时器启动,自动触发timeout信号,每次触发timeout信号更新LCD显示
    connect(_timer, &QTimer::timeout, [=]() {
       static int i = 0;
       i++;
       ui->lcdNumber->display(i);
    });

    //子线程数据处理结束会发送isDone信号,这时我们暂停定时器.
    connect(_thread, &MyThread::isDone, this, &Widget::DealIsDone);

}

Widget::~Widget() {
    delete ui;
}

//由于线程号是有限的,线程用完之后要将其关闭
void Widget::DealIsDone() {
    _timer->stop();
    //停止线程
    _thread->quit();
    //回收子进程资源
    _thread->wait();
}

void Widget::on_pushButton_clicked() {
    //如果定时器没有工作才启动
    if (_timer->isActive() == false) {
        _timer->start(100);
    }

    //启动子线程同时进行数据处理
    _thread->start();
}

这样,我们就可以LCD显示和数据处理同时进行,这样就可以知道数据处理的时间了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cD4UYfUA-1662045421467)(C:\Users\84995\AppData\Roaming\Typora\typora-user-images\1662024613522.png)]

Qt5实现多线程的方法

Qt5实现多线程的方法

例:背景是,我们将定时器写成子线程,然后主线程就用来显示LCD的值。

1.设定一个类,继承于QObject

2.类中设置一个线程函数(只有一个线程函数)

//MyThread
class MyThread : public QObject {
public:
    void myTimer() {
        while (1) {
            emit mySignal();
        }
    }
signals:
    void mySignal(); //类似定时器的timeout信号
    void startThread(); //启动线程信号
}

3.主线程中创建线程对象(不能指定父对象)

4.主线程中创建一个QThread子线程对象

5.把自定义线程类加入到子线程

6.启动子线程,并发射启动子线程信号

7.自定义线程函数通过与发射的子线程信号连接来实现子线程。(不能直接调用线程处理函数,直接调用线程处理函数,会导致线程处理函数和主线程在同一个线程,只能通过信号和槽的方式来调用)

子线程的创建

//mythread.h
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include <QDebug>

class MyThread : public QObject {
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void dealTimeOut();

    void setFlag(bool flag);
signals:
    void myTimeOut();
public slots:

private:
    bool _isStop = false;
};

#endif // MYTHREAD_H
//myThread.cpp
#include "mythread.h"

MyThread::MyThread(QObject *parent) : QObject(parent) {

}

void MyThread::dealTimeOut() {
    while (!_isStop) {
        //每隔1s发送一个信号
        QThread::sleep(1);
        emit myTimeOut();
        qDebug() << "子线程号:" << QThread::currentThread() << endl;
    }
}

void MyThread::setFlag(bool flag) {
    _isStop = flag;
}

主线程调用子线程

有以下注意事项:

  1. 分别创建子线程对象和自定义线程对象。注意自定义线程对象不能指定父类。
  2. 将自定义线程对象通过moveToThread函数移动到子线程对象中。
  3. 不能直接调用自定义线程运行函数,要调用自定义线程处理函数时,采用以下步骤:
    • 启动子线程
    • 主窗口发送启动线程的信号
    • 自定义线程要执行的处理函数作为槽函数被调用。
  4. 线程结束时,也要进行如下步骤:
    • 子线程调用quit()函数结束线程
    • 子线程调用wait回收子线程资源
    • 注意子线程未结束直接关闭主线程,子线程不会关闭,因此关闭主线程时也要把子线程关闭,并且由于自定义线程没有指定父对象,关闭时也要把自定义线程对象delete掉。
//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "mythread.h"
#include <QThread>
#include <QDebug>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

     void dealSignal();

private slots:
    void on_pushButtonStart_clicked();

    void on_pushButtonStop_clicked();

signals:
    void startThread(); //启动子线程的信号

private:
    Ui::Widget *ui;
    //动态分配空间不能指定父对象
    MyThread *_myThread = new MyThread;

    //创建子线程,指定父对象
    QThread *_thread = new QThread(this);
};

#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget) {
    ui->setupUi(this);

    //把自定义线程加入到子线程中
    _myThread->moveToThread(_thread);

    connect(this, &Widget::startThread, _myThread, &MyThread::dealTimeOut);
    connect(_myThread, &MyThread::myTimeOut, this, &Widget::dealSignal);
    connect(this, &Widget::destroyed, this, &Widget::on_pushButtonStop_clicked);
    qDebug() << "主线程号:" << QThread::currentThread() << endl;

}

Widget::~Widget() {
    delete ui;
}

void Widget::dealSignal() {
    static int i = 0;
    ui->lcdNumber->display(i++);
}

void Widget::on_pushButtonStart_clicked() {
    //启动线程,但没有启动线程处理函数
    _thread->start();
    _myThread->setFlag(false);
    //不能直接调用线程处理函数
    //直接调用,导致线程处理函数和主线程在同一个线程
//    _myThread->dealTimeOut();
    //只能通过信号与槽的方式来调用
    emit startThread();
}

void Widget::on_pushButtonStop_clicked() {
    _myThread->setFlag(true);
    _thread->quit();
    _thread->wait();
}

connect()的第五个参数的作用

connect()第五个参数的作用,连接方式:默认,队列,直接,第五个参数只有在多线程时才有意义。

  • 如果是多线程,默认使用队列方式
  • 如果是单线程,默认使用直接方式
  • 队列:槽函数所在的线程和接受者一样。
  • 直接:槽函数所在线程和发送者一样。

线程画图

子线程不能操作图形界面,主要用于后台运行,进行数据处理使用。

背景:子线程用于画图,主线程每次点击画图按钮,更新子线程画的图形

子线程的创建

子线程画完图之后,发射更新图片信号,将画好的图片通过信号传送出去。

//mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QPainter>
#include <QImage>
#include <QBrush>

class MyThread : public QObject {
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void drawImage();

signals:
    void updateImage(QImage img);

public slots:

};

#endif // MYTHREAD_H
//mythread.cpp
#include "mythread.h"

MyThread::MyThread(QObject *parent) : QObject(parent) {

}

//线程处理函数
void MyThread::drawImage() {
    //定义QImage绘图设备
    QImage image(500, 500, QImage::Format_ARGB32);
    //定义画家,指定绘图设备
    QPainter painter(&image);

    //定义画笔对象
    QPen pen;
    pen.setWidth(5);
    painter.setPen(pen);

    //定义画刷对象
    QBrush brush;
    brush.setColor(Qt::blue);
    brush.setStyle(Qt::Dense1Pattern);
    painter.setBrush(brush);

    //定义5个点
    QPoint a[5] {
        QPoint(qrand() % 500, qrand() % 500),
        QPoint(qrand() % 500, qrand() % 500),
        QPoint(qrand() % 500, qrand() % 500),
        QPoint(qrand() % 500, qrand() % 500),
        QPoint(qrand() % 500, qrand() % 500)
    };


    painter.drawPolygon(a, 5);

    //通过信号发送图片
    emit updateImage(image);

}

主线程调用子线程

主线程调用子线程步骤如下:

  1. 创建子线程对象和自定义函数处理对象。并将自定义函数处理对象moveToThread到创建出的子线程中。
  2. 开启子线程,_thread->start(),并通过connect连接线程处理函数。
  3. 子线程处理函数处理结束后,发送处理结束信号updateImage,主线程接受这个信号,并将接收到的图片画到设备上。
  4. 关闭子线程。
//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPaintEvent>
#include <QPainter>
#include <mythread.h>
#include <QThread>

namespace Ui {
class Widget;
}

class Widget : public QWidget {
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    //重写绘图事件
    void paintEvent(QPaintEvent *event);

public slots:
    void slotGetImage(QImage img);

    void slotClose(); //窗口关闭槽函数

private:
    Ui::Widget *ui;
    QImage _image;
    MyThread *_myThread = new MyThread;
    QThread *_thread = new QThread(this);
};

#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget) {
    ui->setupUi(this);

    //将自定义模块添加到子线程
    _myThread->moveToThread(_thread);

    //启动子线程,但是并没有启动线程处理函数
    _thread->start();

    //线程处理函数,必须通过signal-slot调用
    //drawImage槽函数和接受者_myThread在同一个子线程,而_myThread在子线程_thread中,因此就连接到了子线程。
    connect(ui->pushButton, &QPushButton::clicked, _myThread, &MyThread::drawImage);
    connect(_myThread, &MyThread::updateImage, this, &Widget::slotGetImage);
    connect(this, &Widget::destroyed, this, &Widget::slotClose);
}

Widget::~Widget() {
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event) {
    QPainter painter(this); //指定绘图设备为窗口
    painter.drawImage(50, 50, _image);
}

void Widget::slotGetImage(QImage img) {
    _image = img;
    update(); //更新窗口,间接调用paintEvent
}

void Widget::slotClose() {
    //结束线程
    _thread->quit();
    //回收线程资源
    _thread->wait();
    //销毁自定义线程类对象
    delete _myThread;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt的多线程编程 的相关文章

  • BERT模型的详细介绍

    1 BERT 的基本原理是什么 xff1f BERT 来自 Google 的论文Pre training of Deep Bidirectional Transformers for Language Understanding xff0c
  • 自然语言处理(NLP)之使用TF-IDF模型计算文本相似度

    自然语言处理 NLP 之使用TF IDF模型计算文本相似度 所用数据集 xff1a ChnSentiCorp htl all csv 语料库即存放稀疏向量的列表 要注意的是 xff0c 搜索文本text与被检索的文档共用一个特征词词典 NL
  • C++中关于类重复定义的分析和解决方法

    在C 43 43 中将类以及类中的成员函数的声明放在 h的头文件中 xff0c 而将类中成员函数的定义 xff08 即实现代码 xff09 放在 cpp的源文件中 xff0c 这样我们的程序设计起来更加的模块化 xff0c 但是 xff0c
  • re.search()用法详解

    re search xff1a 匹配整个字符串 xff0c 并返回第一个成功的匹配 如果匹配失败 xff0c 则返回None pattern 匹配的规则 string 要匹配的内容 flags 标志位 这个是可选的 就是可以不写 可以写 比
  • re.findall()用法详解

    re findall xff1a 函数返回包含所有匹配项的列表 返回string中所有与pattern相匹配的全部字串 xff0c 返回形式为数组 示例代码1 xff1a 打印所有的匹配项 import re s 61 34 Long li
  • Linux系统中创建虚拟环境详解

    1 方法一 1 1 安装虚拟环境的命令 xff1a sudo pip install virtualenv sudo pip install virtualenvwrapper 1 2 安装完虚拟环境后 xff0c 如果提示找不到mkvir
  • 使用python将图片改为灰度图或黑白图

    使用python将图片改为灰度图或黑白图有三种方式 xff0c 分别是是使用cv2库和PIL库来实现 xff0c 详细过程如下所示 1 使用cv2库将图片改为灰度图 在使用cv2进行读取原彩色图片时 xff0c 在里面添加一个参数cv2 I
  • 虚拟机中windows镜像下载与安装

    镜像文件下载 xff1a 链接 xff1a https pan baidu com s 1VKWMHHCGRwWXk2GpxyUp0A 提取码 xff1a shlg 注意 xff1a 虚拟机中的镜像和本地电脑系统安装的镜像是一样的 安装教程
  • mongo数据库中字符串型正负数值比较大小

    数据库中数据展示 xff1a 使用python代码实现 xff1a Requires pymongo 3 6 0 43 from pymongo import MongoClient client 61 MongoClient 34 mon
  • flask项目中内部接口调用其他内部接口操作

    1 requests 在 Flask 框架项目中 xff0c 可以通过使用 requests 模块来进行内部接口调用 requests 模块是 Python 中常用的 HTTP 请求库 xff0c 可以用于发送 HTTP 请求和处理响应 示
  • ElasticSearch删除索引中的数据(delete_by_query)

    1 删除两个月以前的数据 在 Elasticsearch 中 xff0c 要删除两个月以前的数据 xff0c 可以通过以下步骤 xff1a 计算当前时间的两个月前的日期 xff0c 可以使用 Python 的 datetime 模块来实现
  • Qt Creator子图绘制

    Qt中在一个窗体文件内画所有图显然是不好维护的 xff0c 我们可以将主窗体拆分为几个子窗体 xff0c 在子窗体中绘制子图 xff0c 这样便于我们去维护我们的代码 1 在工程文件中右键 gt Add New 2 选择Qt 设计师界面 3
  • MessageFilter [target=odom ]: Dropped 100.00% of messages so far.问题解决

    错误提示 WARN 1580994954 426403779 MessageFilter target 61 odom Dropped 100 00 of messages so far Please turn the ros gmappi
  • 电磁循迹智能车基于stm32cubeMX、HAL库—我的第一辆智能车

    我的第一辆智能车 电磁循迹智能车 提示 本文适用于初学 想完成一个基础四轮车练练手者 大佬还请勿喷 不过欢迎提出意见 有纰漏之处我将及时纠正 注 工程代码链接已贴在文末 前言 所用到的硬件平台 stm32f103c8t6 舵机 电机 L29
  • 2022年国赛建模B题思路与程序

    B题 无人机遂行编队飞行中的纯方位无源定位 关键词搜索 xff1a 无人机 xff0c 无源定位 其实这个工作特别多 xff0c 知网一堆 xff0c 如果选这个题一定要想好做的出彩 xff0c 另外网上的场景和本题不是很一样 xff0c
  • 2017全国大学生电子设计竞赛:室内可见光定位装置

  • 基于FreeRTOS下多任务的同时操作

    FreeRTOS移植及多任务的实现 前言 xff1a 一 FreeRTOS移植 xff08 1 xff09 移植准备工作 xff08 2 xff09 FreeRTOS移植到stm32中 xff08 3 xff09 例程验证 二 多任务实现
  • undefined symbol 问题解决记录

    历经一个月 xff0c 昨日完成打印机network部分的编写 c语言 xff0c 编写makefile构建动态库 构建完成后遂进行调用测试 xff0c 出现 xff1a network symbol lookup error usr li
  • 2.O(NlogN)的排序算法

    认识O NlogN 的排序算法 1 剖析递归行为及其时间复杂度的估算 递归过程 xff1a 递归过程是一个多叉树 xff0c 计算所有树的结点的过程就是利用栈进行后序遍历 xff0c 每个结点通过自己的所有子结点给自己汇总信息之后才能继续向
  • 4.二叉树的遍历(C++版)

    二叉树的递归 1 二叉树递归遍历 二叉树的递归序 递归序过程 xff1a 两个注释1之间的代码代表第一次来到一个节点的时候 xff0c 会判断一下这个节点是否为空 xff1b 来到这个节点的左树去遍历 遍历完第二次回到本函数 xff0c 进

随机推荐

  • 6.暴力递归转动态规划

    动态规划 1 什么是动态规划 xff1f 动态规划就是暴力递归 xff08 回溯 xff09 的过程中有重复调用的过程 xff0c 动态规划在算过每次调用后把答案记下来 xff0c 下次再遇到重复过程直接调用这个行为就叫动态规划 动态规划就
  • 8.岛问题

    岛问题 题目 一个矩阵中只有0和1两种值 xff0c 每个位置都可以和自己的上 下 左 右四个位置相连 xff0c 如果有一片1连在一起 xff0c 这个部分叫做一个岛 xff0c 求一个矩阵中有多少个岛 xff1f 例子 0 0 1 0
  • 9.KMP算法

    KMP算法 1 KMP算法解决的问题 字符串str1和str2 xff0c str1是否包含str2 xff0c 如果包含返回str2在str1中开始的位置 xff0c 如果不包含返回 1 如果做到时间复杂度O N 完成 xff1f 测试用
  • 10.Manacher算法(用于解决回文子串问题)

    Manacher算法 1 Manacher算法解决的问题 字符串str中 xff0c 最长回文子串的长度如何求解 xff1f 如何做到时间复杂度O N 完成 xff1f 回文序列是从左往右和从右往左看一样 xff0c 如abba xff0c
  • git push代码到远程仓库,报错解决:fatal: unable to access ‘https://github.com/.......‘: OpenSSL SSL_read: Connec

    报错如下 xff1a 产生原因 xff1a 一般是这是因为服务器的SSL证书没有经过第三方机构的签署 xff0c 所以才报错解除ssl验证后 xff0c 再次git即可 解决办法输入此条git命令 xff1a git config glob
  • 11.滑动窗口的最大值——重要结构双端队列

    滑动窗口最大 xff08 小 xff09 值 1 滑动窗口最大值结构 窗口概念 xff1a 一开始窗口左边界L 有边界R都停留在数组左侧 xff0c 窗口L和R都只能往数组右边移动 xff0c 并且左边界L永远不能超过有边界R 任何时刻都能
  • 12.单调栈——解决接雨水和柱状图中的最大矩形等问题

    单调栈 1 单调栈实现结构 单调栈解决的问题 xff1a 给你一个数组 想要用尽可能低的代价知道数组中每一个元素的左边元素比它大的或者右边元素比他大的信息是什么 如果用暴力方法 xff0c 左边遍历一次右边遍历一次 xff0c 时间复杂度为
  • 12.快速排序

    1荷兰国旗问题 问题1 xff1a 给定一个数组arr和一个数num xff0c 将小于等于num的数放在数组的左边大于num的数放在数组的右边 xff08 不要求有序 xff09 要求额外空间复杂度为O 1 时间复杂度为O N 遍历数组元
  • 死锁预防、死锁避免、死锁检测

    死锁 1 死锁的概念 1 1死锁的定义 多个进程并发执行 xff0c 由于竞争资源而造成的一种僵局 xff08 互相等待 xff09 xff0c 若无外力作用 xff0c 这些进程都将无法推进 xff0c 这就是死锁现象 例如 xff1a
  • 内存分配方式

    内存分配方式 1 基本概念 内存管理的基本概念 虽然计算机硬件发展 xff0c 内存容量在不断变大 xff0c 但是也不可能将所有用户进程和系统所需要的程序和数据放入内存中 xff0c 因此操作系统必须要对内存空间进行合理划分和有效动态分配
  • 虚拟内存和LRU页面置换算法

    虚拟内存 1 虚拟内存的基本概念 传统存储管理方式的特征 传统的内存管理策略都是为了同时将多个进程保存进内存中 xff0c 它们具有以下的共同特征 xff1a 一次性 作业必须一次性全部装入内存后 xff0c 才能开始运行 xff08 静态
  • 0.0C++和C的区别

    C 43 43 和C的区别 C 43 43 如今是一个同时支持面向过程 面向对象 函数形式 泛型形式 元编程形式的语言 我们该如何理解C 43 43 这门语言呢 xff1f Effective C 43 43 书中给出了一个简单的方法 xf
  • 15.9为什么要将成员变量设置为private

    为什么要将成员变量声明为private 为什么要将成员变量封装为private xff0c 主要有以下四个原因 xff1a 好处1 xff1a 如果成员变量不是public xff0c 那么客户唯一能访问成员变量的唯一方式就是通过成员函数
  • 2.7.C++中static关键字的5种基本用法

    static关键字 static关键字主要应用于以下几种情况 xff1a 情况1 xff1a static静态函数 定义静态函数 xff1a 在函数返回类型前加上static关键字 xff0c 函数即被定义为静态函数 静态函数只能在本源文件
  • 进程调度算法

    进程调度 在多道程序系统中 xff0c 进程数量往往多于处理机的个数 xff0c 因此进程竞争使用处理机的情况在所难免 处理机调度是对处理机进行分配 xff0c 即从就绪队列中按照一定的算法选择一个进程并将处理机分配给它运行 xff0c 以
  • git clone 出现fatal: unable to access ‘https://github 类错误解决方法

    git clone 遇到问题 xff1a fatal unable to access https github comxxxxxxxxxxx Failed to connect to xxxxxxxxxxxxx 问题 将命令行里的http
  • 进程通信的方式

    进程通信 1 进程通信的概念 进程是一个独立的资源分配单元 xff0c 不同进程 xff08 主要是指不同的用户进程 xff09 之间的资源是独立的 xff0c 没有关联的 xff0c 不能在一个进程中直接访问另一个进程的资源 但是 xff
  • 网络通信的过程

    网络通信的过程 封装 上层协议时如何使用下层协议提供的服务的呢 xff1f 其实这是通过封装实现的 应用程序是在发送到物理网络上之前 xff0c 将沿着协议栈从上往下依次传递 每层协议都将在上层数据的基础上加上自己的头部信息 xff08 有
  • TCP三次握手、四次挥手

    TCP通信流程 TCP和UDP TCP和UDP区别如下 xff1a UDP xff1a 用户数据报文协议 xff0c 面向无连接 xff0c 可以单播 xff0c 多播 xff0c 广播 xff0c 面向数据报 xff0c 不可靠 TCP
  • Qt的多线程编程

    Qt线程 基本概念 并发 当有多个线程在操作时 xff0c 如果系统只有一个CPU xff0c 则它根本不可能真正同时进行一个以上的线程 xff0c 它只能把CPU运行时间划分成若干个时间段 xff0c 再将时间段分配给各个线程执行 xff