【Linux】线程概念详析

news2024/11/25 2:31:11

我们已经了解了Linux操作系统进程部分相关知识:

博主有关Linux进程相关介绍的文章:

  • 💥[Linux] 系统进程相关概念、系统调用、Linux进程详析、进程查看、fork()初识

  • 💥[Linux] 进程状态相关概念、Linux实际进程状态、进程优先级

  • 💥[Linux] 什么是进程地址空间?父子进程的代码时如何继承的?程序是怎么加载成进程的?为什么要有进程地址空间?

  • 💥[Linux] 详析进程控制:fork子进程运行规则?怎么回收子进程?什么是进程替换?进程替换怎么操作?

通过阅读这几篇文章, 至少可以让我们对Linux系统中的进程 有一个最基本又相对全面的认识.

但是今天这篇文章, 可能又会对之前介绍过的进程多少有一些推翻.

本篇文章的主要内容是:Linux操作系统中, 有关多线程的相关介绍.


文章目录

  • Linux线程概念
    • 什么是线程
    • Linux进程-再理解
    • Linux线程的创建、查看
      • pthread_create 和 pthread_join
    • 线程相关概念总结
    • 线程的优点
    • 线程的缺点
    • 线程异常
  • Linux进程 VS 线程

Linux线程概念

线程可以说是实际区别于进程的一个概念, 但也可以说是实际没有区别于进程的一个概念.

而实际区别与否, 其实 与平台有关

什么是线程

有关线程的概念, 大概可以通过三个要点介绍:

  1. 线程是在进程内部运行的执行流
  2. 线程相比进程, 粒度更细, 调用成本更低
  3. 线程是CPU调度的基本单位

不过这三个要点只能让你大概的对线程有一个最最最基本的认识:线程比进程要小. 但具体怎么小 是不知道的.

不过可以举个例子来简单的介绍一下, Linux下的线程:

在之前有关进程的介绍中, Linux系统中的进程 = PCB + 被加载到内存中的程序数据, 不过 PCB和内存中的程序数据 并不是直接相映射的.

之间还要通过 进程地址空间和相应的页表, 不过CPU实际只是是通过访问PCB 来实现对进程的调度的:

PCB(task_struct)中描述着进程地址空间, 进程地址空间与物理内存 通过两张页表来相互映射.

这是Linux系统中, 单个进程实际在操作系统中的存在形式.

系统创建进程会创建这所有的格式和数据.

不过我们也介绍过, 如果通过fork创建子进程, 在未作数据修改时 子进程与父进程是共享进程的数据和代码的. (子进程也存在自己的进程地址空间和页表, 只不过指向同一块数据和代码)

而且, 我们可以通过对fork()返回值的判断, 让父子进程执行不同的代码块.

这, 其实说明了一个 细节不同的执行流, 可以做到执行不同的资源, 即 可以做到对特定资源的划分

那么, 如果下次创建进程, 操作系统并不创建有关进程的所有结构, 而是只创建 PCB. 将新的PCB 指向已经存在的进程.

不同PCB指向同一个进程地址空间 - CPU与PCB之间的虚线表示, 此PCB也被CPU调度, 但当前可能没有被调度

然后, 以子进程划分程序资源类似的手段, 将进程的代码划分为不同的区域, 并将不同的PCB设置为实际分别负责执行不同的区域的代码:

PCB与代码区之间连接的红色虚线表示, PCB实际负责执行的代码区域

最终, 不同的PCB可以访问进程地址空间内代码区的不同区域, 并通过相应的页表来访问到实际的物理内存.

实际上这样就在进程内部创建了多个PCB执行流, 而每个PCB执行流都只能访问一小部分的代码一小部分的页表.

那么, 在Linux操作系统中, 我们就 可以将这样的PCB执行流称作 "线程".

这里只是介绍了一下Linux操作系统中, 线程的 粗粒度 的实现原理


介绍了Linux平台下 线程的粗粒度的实现原理, 我们应该可以理解一个内容:线程 : 进程 = N : 1

操作系统 对 比线程数量要少的进程 都会用 PCB 将进程的所有属性 描述、组织、管理起来, 那么对线程, 毫无疑问也是需要用一个结构体描述、组织、管理起来的. 在大多数的操作系统中, 描述线程的结构体叫做 TCB

如果一个操作系统, 为了描述管理进程和线程, 在内核分别实现了不同的 PCBTCB. 那么 PCBTCB 之间一定存在非常复杂的耦合关系. 因为 PCB 描述一个进程, 而 TCB 描述进程内部的线程. 这两部分一定存在相当一部分的重叠属性, 还有一定的包含关系.

那么, 在以后维护一个进程与其内线程的关系时, 一定是一个非常复杂的维护过程.


其实文章介绍线程概念到现在, 一举到具体的例子, 就在强调 在Linux操作系统下.

什么原因呢? 其实 不同操作系统实现线程的方式可能是不同的.

我们在上面提到, 操作系统会存在一个描述线程属性的结构体, 以维护线程.

但是, 实际上 并不是所有的操作系统都会对线程另外描述一个结构体, 使TCB与PCB之间的关系变得非常复杂.

Linux操作系统 就没有另外实现一个描述线程的结构体, 而是 用task_struct(进程PCB)模拟了线体. 即Linux操作系统中, 描述进程和描述线程的结构体实际上是同一个结构体: task_struct

而我们常用的 Windows操作系统, 则是真正将进程与线程区分开, 分别实现了PCB和TCB 以分别用来维护线程和进程. 这样的被称为 真·线程 操作系统

为什么不同的操作系统会对进程和线程之间的关系, 设计出这样的差别呢?

其实是开发者对 进程和线程在执行流层面的理解不同.

以 Windows 来说, Win为了维护线程真正实现了一个不同于PCB的TCB. 也就是说, Win的开发者认为进程和线程在执行流层面是不同的东西. 进程有自己的执行流, 线程在进程内部也有自己的执行流

而 Linux 则认为 进程和线程在概念上不做区分, 都是执行流. PCB要不要被CPU调度?TCB要不要被CPU调度?PCB调度要不要优先级?TCB要不要?要不要通过PCB找到代码和数据?要不要通过TCB找到代码和数据?进程切换要不要保护进程的上下文数据?线程切换要不要保护上下文数据?……

在Linux看来, 种种迹象表明 PCB和TCB的功能 不从更细节来细分的话, 其实是大致相同的. 无非就是PCB和TCB中描述的代码量和数据量的不同, 所以 进程和线程都只看成一个执行流.

所以 Linux线程, 其实就使用task_struct(进程PCB)模拟实现的.

只不过, 线程的TCB(实际上还是PCB)只能访问执行 整个进程中的一小块的代码和数据

这样做有什么好处?

用进程PCB模拟实现线程, 对线程 可以复用操作系统中已经针对进程实现的各种调度算法, 因为进程和线程的描述结构是相同的.

也不用维护进程和线程之间的关系.

也就是说, Linux操作系统中 线程TCB底层就可以看作进程PCB

Linux复用PCB实现TCB, 那么从CPU的角度看待线程, 其实与进程没有区别. CPU调度线程实际上看到的还是PCB(task_strcut)


Linux进程-再理解

上面已经介绍了, Linux中 线程使用进程PCB来模拟实现的, 那么现在又该如何理解进程呢?

在没有介绍线程之前, 我们可以说 CPU看到的所有task_struct都是一个进程

而现在, CPU看到的所有task_struct都是一个执行流

之前我们说, 进程 = PCB + 内存中对应的代码和数据.

而现在, 我们知道进程内部可以存在许多task_srtuct, 那么又可以怎样理解进程呢?

现在, 不能只认为 PCB + 代码和数据 就是一个进程. 而是 需要理解, 上图中的所有结构加起来才能叫一个进程.

我们可以说, 进程是 承担操作系统资源分配的基本实体. 即 进程是 向系统申请资源的基本单位

在没有介绍线程时, 我们可以说 PCB可以表示一个进程, 因为之前进程只有一个执行流, 即只有一个task_struct.

现在 我们可以称 只有一个执行流的进程 为 但单执行流进程, 称 内部存在多个执行流的进程 为 多执行流进程

那么现在, 以CPU的视角来再次看待 task_struct, 我们 现在理解的CPU看到的task_struct 比 没有介绍线程时CPU看到的task_struct 体量要小

因为 Linux中, 现在我们理解的CPU看到的 task_struct 可能是 线程, 可以看作是 轻量化的进程

进程是向系统申请资源的基本单位, CPU调度进程是通过 PCB(task_struct) 调度的, 所以 现在我们说 线程, 是CPU调度的基本单位

那么此时, 我们应该就可以理解 有关线程的概念三个要点介绍:

  1. 线程是在进程内部运行的执行流

    线程只访问执行进程的一部分数据和代码

  2. 线程相比进程, 粒度更细, 调用成本更低

    进程切换调度, 需要切换PCB、进程地址空间、页表等

    而线程切换调度, 只需要切换TCB(实际还是PCB)就可以

  3. 线程是CPU调度的基本单位

Linux线程的创建、查看

介绍线程介绍了这么多, 那么 Linux中如何创建并查看线程呢?

下面我们来直接简单演示一下, 不做太多的介绍. 只创建和查看线程.

pthread_create 和 pthread_join

Linux操作系统为我们提供了创建线程的系统调用:

int pthread_create(pthread_t *thread, const pthread_attr *attr, void *(*start_routine)(void *), void *arg);

这个接口看起来, 非常的复杂

不过, 实际上也没有太复杂. pthread_t 就是一个无符号长整型:

|inline

第一个参数就是此类型的指针, 第一个参数是一个输出型参数, 用于获取线程id

第二个参数, 是线程属性结构体的指针, 暂时不过多介绍 现在我们传入 nullptr

第三个参数, 返回值为空指针 参数为空指针的 函数指针, 用于 传入此线程需要执行的函数.

第四个参数, 一个空指针, 此空指针其实就是 第三个参数(函数指针)所指向的函数的参数

处理创建线程之外, 线程与子进程一样, 还需要等待:

int pthread_join(pthread_t thread, void **retval);

此函数的参数很简单:

  1. 第一个参数传入 需要等待的线程的id
  2. 第二个参数接收退出结果, 暂时不关心. 我们只是看一看现象

简单的了解之后, 我们就可以使用此接口 创建线程:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
using std::cout;
using std::endl;
using std::string;

void* threadFun1(void* args) {
    string str = (char*)args;
    while (true) {
        cout << str << ": " << getpid() << " " << endl;
        sleep(1);
    }
}

void* threadFun2(void* args) {
    string str = (char*)args;
    while (true) {
        cout << str << ": " << getpid() << " " << endl;
        sleep(1);
    }
}

int main() {
    pthread_t tid1, tid2;

    pthread_create(&tid1, nullptr, threadFun1, (void*)"thread_1");
    pthread_create(&tid2, nullptr, threadFun2, (void*)"thread_2");
    sleep(1);

    while (true) {
        cout << " 主线程运行: " << getpid() << " " << endl;
        sleep(1);
    }

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);

    return 0;
}

不过, 我们编译时会发现有错误:

是连接错误, 为什么呢?

其实 man 手册中已经说了, 使用pthread_create() 接口, 编译连接时需要链接 pthread 库

因为, pthread 是第三方库, 所以我们需要手动链接:

此时, 编译链接成功.

我们运行程序:

thread_show

可以看到线程在分别运行, 所输出的进程pid都是相同的.

输出结果可能会混乱, 可能与线程的优先级与CPU核心、线程数有关

输出结果混乱, 也说明了线程可以并行运行

我们进程运行时查看系统的进程表:

系统中只有一个有关threadTest的进程

可以看到, 有关threadTest 的进程只有一个.

即, 只有一个进程但是进程内存在多个线程.

那么如何查看线程呢?

我们可以在命令行使用 ps -aL 命令 来查看线程(a: all, -L: 轻量级进程):

可以看到, 线程列表中 存在三个相同命令名的线程. 且这三个线程同时属于一个PID 23412. 还拥有各自的 LWP 轻量级进程编号.

并且此 有一个线程的LWP与PID相同, 表示此线程是主线程

有兴趣的话, 可以在创建两个线程之后, 再创建一个子进程.

创建之后, 观察子进程有没有什么地方与之前创建的子进程时的情况不同

线程相关概念总结

上面 从 介绍线程 到 Linux中的线程 再到 Linux线程查看, 已经分析了很多.

但是似乎还是不能对什么是线程、线程的特点做出一个总结, 那么究竟什么是线程呢?

  1. 线程是程序内部的一个执行线路, 更准确一点的定义是:线程是进程内部的控制序列
  2. 一切进程, 至少都有一个线程
  3. 线程在进程内部运行, 本质是在进程地址空间内运行
  4. Linux操作系统中, CPU看到的PCB都比传统意义的PCB要轻量化. 因为Linux中的PCB可能表示的只是一个线程
  5. 透过进程地址空间是可以看到进程的, 将进程资源合理的分配给每一个进程内部的执行流, 就形成了线程执行流

虽然我们说 Linux操作系统中的线程使用进程PCB模拟实现的, 不过其实在设计进程PCB时已经考虑到了线程.

也就是说, 其实PCB(task_struct)内部其实是有用来表示线程的东西的:

task_struct内部, 线程专用的结构体

thread_struct{}结构体内部存储的大部分都是寄存器相关信息. 与维护不同线程的上下文数据有关系

Linux内核源码中, 有关于task_struct内部的成员其实我们已经可以看懂一部分了. 可以尝试去分辨一下成员都代表什么

线程的优点

Linux操作系统中其实可以创建多进程来分配代码并执行, 就比如我们创建子进程并让其执行指定部分的代码.

那么为什么还要有线程呢?其实是因为, 多线程相比进程有一定的优势:

  1. 创建一个新线程的成本比创建一个新进程的成本小的多

创建一个新进程, 操作系统需要分别创建PCB、进程地址空间、页表, 如果对数据做了修改还需要写时拷贝等

而创建一个新线程, 则只需要创建一个PCB就可以了, 进程地址空间、页表、数据等都直接使用原进程的就可以

  1. 与进程之间的切换相比, 线程之间的切换需要操作系统做的工作也会少很多

如果CPU需要切换进程运行, 那么不仅需要切换PCB还需要切换页表等诸多的数据

而切换线程的话, 就只需要切换PCB就可以了

  1. 线程占用资源比进程要少很多

    还是那个原因, 多线程是公用一个进程地址空间和同一页表运行的, 而每个进程都拥有自己的进程地址空间和页表

  2. 对于计算密集型应用, 为了能在多处理器系统上运行, 会将计算分解到多线程去实现

    比如文件加密应用, 可以用多线程将加密工作拆分, 加密完成之后再将文件合并, 就可以完成加密

  3. 对于I/O密集型应用, 为了提高性能, 将I/O操作重叠. 线程可以同时等待不同的I/O操作

    比如一个程序运行时, 需要等待操作系统和网卡之间的I/O操作, 又要等待操作系统和磁盘之间的I/O操作.

    如果单线程的话, 这两个I/O操作只能一个一个等, 不过, 如果是多线程的话就可以同时等待不用排队.

不过, 线程并不是越多越好, 与平台有关, 更准确一点就是与 CPU有关

一般 线程数最好小于等于CPU支持的多线程数.

一般来说, 有多少CPU就可以支持多少线程同时工作.

不过现在CPU都可以模拟多线程, 一个CPU也可能模拟出多线程.

线程的缺点

虽然线程有许多的优点, 但是线程也是存在很大的缺点的:

  1. 可能造成性能损失

    比如一个密集计算型线程正在运行, 且很少或不会被其他外部事件阻塞. 那么这类线程往往是无法与其他线程共用一个CPU的.

    如果密集计算型线程的数量比CPU支持的多线程数量还要多, 这些线程就可能不停的被CPU调度:不停的换出、换入. 因为这些线程都是要运行一下的, 不会只照着一部分线程一直运行, 而是会这一部分执行执行、那一部分执行执行. 这就会因为不停调度而造成性能损失.

    最好线程不要太多.

  2. 健壮性低

    如果是进程, 由于进程地址空间的存在 进程是非常健壮的, 一个进程再怎么运行如果不是刻意为之一般也无法影响另一个进程.

    一个多线程程序内, 可能会因为 时间分配的细微偏差、共享了某些不该共享的数据, 而对其他线程或整个程序造成很大的不良影响.

  3. 缺乏访问控制

    操作系统中, 进程是访问控制的基本粒度, 因为进程具有独立性. 多线程访问可能会同时访问同一个数据, 而且很有可能出大问题

  4. 编程难度高

上面就是多线程的缺点, 不过这些缺点除了第一条, 其他的其实都是对编写者素质的高要求, 什么缺乏访问控制会影响其他线程或整个进程. 其实就是BUG率要高一些. 这就对程序员的素质有较高的要求了.

线程异常

有关线程异常, 其实可以从一个方面理解:

一个多线程进程中, 虽然一般每个线程访问执行的代码和数据不同, 但这些代码和数据都是属于整个进程的, 只有一份.

如果线程出现了异常, 那就说明什么?就说明是进程某处代码出现了异常.

也就是所, 线程出现异常是会影响整个进程 的. 线程出现异常其实就是进程出现了异常.

线程出现异常, 操作系统就会像线程发送信号, 然后会将整个进程终止. 整个进程终止, 进程中的其他所有线程也会退出.

Linux进程 VS 线程

文章已经介绍过了Linux下线程的概念, 那么结合之前介绍的Linux进程.

我们来对比一下, 进程和线程有什么是相同的, 什么是不同的.

  • 进程是系统资源分配的基本单位

  • 线程是调度的基本单位

  • 多线程共享进程数据, 不过不同线程也有自己的一部分数据:

    • 线程ID

      就像每个进程都有自己的ID一样, 每个线程也都有自己的ID

    • 一组寄存器

      每个线程都有一组寄存器, 用来维护线程的上下文数据

    • 线程栈

      进程在运行时, 都会有自己的栈结构, 来给函数的压栈、临时变量等数据提供空间

      其实每个线程也都会维护自己的栈区, 因为线程也可能会不停的函数调用等操作. 所以是需要维护自己的栈区的.

    • errno

    • 信号屏蔽字

      上面介绍信号异常时提到, 线程异常 就是 进程异常. 线程异常操作系统会向线程发送信号.

      不过线程是与进程共享信号处理方法的, 所以一般情况下线程异常 也就是进程异常

      不过, 虽然线程与进程共享信号处理方法, 但是线程是有自己的信号屏蔽字的.

      也就是说, 操作系统向线程和进程发送同一信号, 可能进程会递达, 而线程却会阻塞.

    • 调度优先级

  • 线程和进程会共享这些资源:

    • 代码和数据

      进程中定义的函数, 每个线程都可以调用. 进程中定义的全局变量, 每个线程也都可以访问

    • 文件描述符表

      虽然 文件描述符表并不是进程地址空间内的数据 而是内核数据(在PCB中维护)

      但是 进程的文件描述符表 也是与线程共享的, 线程PCB会指向主线程PCB的文件描述符表

    • 信号的处理方法

    • 进程当前运行路径

    • 用户ID和组ID


文章到这里, 其实Linux线程概念的部分 就已经介绍的差不多了.

感谢阅读~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/426086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【超算/先进计算学习】日报1

目录今日已完成任务列表遇到的问题及解决方案任务完成详细笔记Darknet框架优化介绍darknet介绍YOLO高性能计算与超级计算机简介算力超级计算机概念与体系结构并行编程技术Linux常用操作命令Linux操作系统与指令使用机器信息查询文件、目录和权限文件内容查看环境变量使用对自己…

15.枚举

枚举 枚举是Java1.5引入的新特性&#xff0c;通过关键字enum来定义枚举类。枚举类是一种特殊类&#xff0c;它和普通类一样可以使用构造方法、定义成员变量和方法&#xff0c;也能实现接口,但枚举类不能继承其他类。枚举是一个被命名的整型常数的集合&#xff0c;用于声明一组…

Linux系统下安装zookeeper教程

将下载好的zookeeper上传到linux服务器上&#xff0c;这里使用的是FileFilla 解压&#xff1a;[rootcentos7964 software]# tar -zxvf apache-zookeeper-3.7.1-bin.tar.gz 进入刚刚解压后的文件夹&#xff0c;创建一个名字为data的文件夹&#xff0c;如下&#xff1a; [rootc…

分库分表--shardingjdbc

文章目录前言一、shardingjdbc简介作用二、如何使用1.我有个表现在体量太大了,我想做分库分表2.开始改造1 引入shardingjdbc2 更改yml文件3 测试看效果3.旧数据迁移4.其他分库类型5 部分配置说明总结前言 当项目开始的时候,没有想到后续的分库分表的话,其实对于后续的分库分表…

MYSQL学习 - DDL数据库操作

前言 从今天开始, 健哥就带各位小伙伴学习数据库技术。数据库技术是Java开发中必不可少的一部分知识内容。也是非常重要的技术。本系列教程由浅入深, 全面讲解数据库体系。 非常适合零基础的小伙伴来学习。 ------------------------------前戏已做完&#xff0c;精彩即开始---…

【数据结构】树和二叉树——堆

目录 &#x1f349;一.树的概念及结构&#x1f349; 1.树的概念 2.树的相关术语 3.树的表示 4.树在实际中的应用 &#x1f34a;二.二叉树的概念和结构&#x1f34a; 1.二叉树的概念 2.特殊的二叉树 2.1.满二叉树 2..2.完全二叉树 3.二叉树的性质 4.二叉树的存储结构 …

限流算法(计数器、滑动时间窗口、漏斗、令牌)原理以及代码实现

文章目录前言1、计数器&#xff08;固定时间窗口&#xff09;算法原理代码实现存在的问题2、滑动时间窗口算法原理代码实现存在的问题3、漏桶算法原理代码实现存在的问题4、令牌桶算法原理代码实现最后本文会对这4个限流算法进行详细说明&#xff0c;并输出实现限流算法的代码示…

一文了解Java ArrayList (源码逐行解析)

介绍 ArrayList 是最常用的 List 实现类&#xff0c;内部是通过数组实现的&#xff0c;它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔&#xff0c;当数组大小不满足时需要增加存储能力&#xff0c;就要将已经有数组的数据复制到新的存储空间中。当从 Arr…

c++积累5-lock_guard使用

1、std:mutex 在了解lock_guard之前&#xff0c;需要先学习下std:mutex,因为lock_guard内部就是使用的std:mutex std:mutex&#xff1a;是一个用于保护共享数据不会同时被多个线程访问的类&#xff0c;它叫做互斥量。我们可以把它看作一把锁&#xff0c;基本使用如下&#xff…

【致敬未来的攻城狮计划】— 连续打卡第四天:e2 studio 使用教程

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 文章目录 系列文章目录 文…

Flutter成不了“顶流明星”的7大理由

Flutter是一款由Google推出的跨平台移动应用开发框架&#xff0c;近年来备受关注。尽管Flutter在某些方面表现出色&#xff0c;但仍然有一些人对它的发展前景表示怀疑。近期一些文章针对Flutter的发展提出了不少质疑和批评&#xff0c;称其难以成为移动应用开发的“顶流明星”&…

【计算机网络—— 安装packet tracer的教程,管理MAC地址和配置路由器的实验步骤以及心得体会】

文章目录安装packet tracer的教程管理MAC地址实验目的 &#xff1a;管理MAC地址实验内容及过程记录&#xff08;一&#xff09;运行Cisco Packet Tracer软件&#xff08;二&#xff09;选择PC终端&#xff08;三&#xff09;运行终端&#xff08;四&#xff09;按表内容对交换机…

Spring Cloud Nacos使用总结

目录 安装Nacos服务器 服务发现与消费 服务发现与消费-添加依赖 服务发现-配置文件 服务发现-注解 服务发现-Controller 服务消费-配置文件 服务消费-注解与Ribbon消费代码 服务消费-运行 配置管理 配置管理-添加依赖 配置管理-配置文件 配置管理-注解 配置管理-…

Raft一致性算法(精简和扩展)

raft一致性算法 文章目录raft一致性算法一、raft简介1.1 raft涉及到的名词1.2 Rpc请求1.3 复制状态机二、raft⼀致性算法2.0 摘要2.0.1 所有服务器需遵守的规则2.0.2 跟随者2.0.3 候选⼈2.0.4 领导人2.0.5 状态2.0.6 特性2.1 raft基础2.2 leader选举2.2.1 集群启动时选举2.2.2 …

微电网两阶段鲁棒优化经济调度方法(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

清华大学ChatGLM-6B部署运行

一、模型介绍 开源项目&#xff1a; GitHub - THUDM/ChatGLM-6B: ChatGLM-6B&#xff1a;开源双语对话语言模型 | An Open Bilingual Dialogue Language Model ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型&#xff0c;基于 General Language Model (GLM) 架构&…

PLC高精度定时器(T法测速功能块)

S7-200 SMART PLC时间间隔指令BGN_ITIME,和CAL_ITIME采用的是系统自带的1ms高精度定时器,PLC里只能调用一次。T法测速和M法测速应用时,都需要高精度时序定时器的支持(当然你也可以采用定时中断的方式获取高精度时序),时间间隔指令请参看下面的博客文章: SMART PLC时间间…

keil5使用c++编写stm32控制程序

keil5使用c编写stm32控制程序一、前言二、配置图解三、std::cout串口重定向四、串口中断服务函数五、结尾废话一、前言 想着搞个新奇的玩意玩一玩来着&#xff0c;想用c编写代码来控制stm32&#xff0c;结果在keil5中&#xff0c;把踩给我踩闷了&#xff0c;这里简单记录一下。…

FlinkSQL的Watermark机制和Checkpoint机制

Watermark机制 Watermark机制&#xff0c;就是水印机制&#xff0c;也叫做水位线机制。就是专门用来解决流式环境下数据迟到问题的。 MonotonousWatermark&#xff08;单调递增水印&#xff09; package day05;import lombok.AllArgsConstructor; import lombok.Data; impor…

【谷粒商城之JSR303数据校验和集中异常处理】

本笔记内容为尚硅谷谷粒商城JSR303数据校验和集中异常处理部分 目录 一、简介 二、SR303数据校验使用步骤 1、引入依赖 2、给参数对象添加校验注解 常见的注解 3、接口参数前增加Valid 开启校验 三、异常的统一处理 四、分组解决校验 1、创建Groups 2、添加分组 …