【Linux多线程】线程的概念

news2024/11/15 10:32:07

【Linux多线程】线程的概念

目录

  • 【Linux多线程】线程的概念
    • Linux线程的概念
      • 什么是线程
        • 重新定义线程和进程
    • 进程地址空间第四讲
      • 线程的优点
      • 线程的缺点
      • 线程异常
      • 线程的用途
    • Linux进程VS线程
      • 进程和线程
      • 关于进程线程的问题
    • Linux线程控制
      • POSIX线程库
      • 创建线程
        • 如何给线程传参?
      • 线程ID及进程地址空间布局
        • 再次谈谈pthread_create中的参数和返回值
        • Linux如何创建一个轻量级进程?
        • 线程栈
      • 线程终止
      • 线程等待 为什么需要线程等待?
    • pthread原生线程库 VS C++11thread库

作者:爱写代码的刚子

时间:2024.3.19

前言:本篇博客将会介绍线程的基本概念,理解线程与进程的区别和联系,以及进程地址空间第四讲

Linux线程的概念

什么是线程

  • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行,本质是在进程地址空间内运行(任何执行流要执行都必须要有资源)
  • 在Linux中,线程的执行粒度要比进程要更细,线程执行进程代码的一部分
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

在这里插入图片描述

CPU只有调度执行流的概念

重新定义线程和进程

什么叫做线程?我们认为,线程操作系统调度的基本单位!

重新理解进程:内核观点:进程是承担分配系统资源的基本实体。(多个执行流,虚拟地址空间,页表,物理内存里面的代码和数据)

进程内部包含线程,进程是承担分配系统资源的基本实体,线程是进程内部的执行流资源

CPU:

线程<=执行流<=进程

linux中的执行流叫做轻量级进程

进程地址空间第四讲

在这里插入图片描述

从物理地址读到CPU内部的地址是虚拟地址

CR3寄存器中的地址指向的是页目录的起始地址。任何一个进程必须要有页目录!

CPU内还有CR2寄存器,里面存放的是引起缺页中断异常的虚拟地址。(因为内存申请建立映射后需要知道上次访问的地址)

【问题】:虚拟地址是如何转换到物理地址的?

以32位虚拟地址为例,虚拟地址为32位:

实际上,32位的虚拟地址可以拆分为10+10+12,

在这里插入图片描述

  • 还有一些系统将页框的大小弄成4MB,我们称为大页式内核,具体的细节参考《深入理解Linux》一书中的“扩展分页”,如果为4MB,那页表还会更小。

但是我们之前对整数进行取地址为什么只拿到了一个地址?C/C++中任意的一个变量或者结构体都只有一个地址(第一个字节的起始地址)。计算机硬件只要能帮我们找到这个变量的起始地址,CPU天然知道要识别几个字节(结构体转化为二进制后没有结构体的概念,也就是内置类型的集合,依旧能识别)

起始地址+类型 = 起始地址 + 偏移量 ————X86的特点

所以线程目前分配资源,本质就是分配地址空间范围。

线程的优点

线程比进程要更轻量化(为什么?)

a. 创建和释放更加轻量化

b. 切换更加轻量化

CPU内部有cache缓存,由于局部性原理,进程在调度的时候会越跑越快(缓存的热数据),因为它的命中率会越来越高。线程切换效率更高,在同一个进程内切换的线程。cache内的数据不需要由冷变热,不需要重新缓存。切换进程会将cache内的数据重新缓存,效率较低。

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现 (一般是有多少个CPU就创建多少个线程)
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

线程的缺点

  • 性能损失
    • 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。(瓜分时间片导致时间片过多,调度频繁)
  • 健壮性降低
    • 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
  • 缺乏访问控制
    • 进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
  • 编程难度提高
    • 编写与调试一个多线程程序比单线程程序困难得多
  • 当进程收到了一个信号,所有线程都要执行对应的处理方法

线程异常

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出

线程的用途

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是 多线程运行的一种表现)

Linux进程VS线程

进程和线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位
  • 线程共享进程数据,但也拥有自己的一部分数据:

每个线程独立的数据:

  • 线程ID
  • 一组寄存器
  • 栈(每个线程独立,不会出现执行流错乱)
  • 线程要有独立的上下文
  • errno
  • 信号屏蔽字
  • 调度优先级

进程的多个线程共享 同一地址空间,因此Text Segment(代码区)、Data Segment(数据区)都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表(重要,一个线程打开,其他的线程也能看到)
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id

进程和线程的关系如下图:

在这里插入图片描述

关于进程线程的问题

  • 如何看待之前学习的单进程? 具有一个线程执行流的进程
  • Linux上没有真正意义上的线程,而是用“进程内核数据结构”模拟的线程(复用进程数据结构和管理算法,用struct task_struct模拟线程)
  • 所以我们通常将线程叫做执行流
  • 内核中没有很明确的线程的概念,只有轻量级进程的概念,不会给我们提供线程的系统调用,只会给我们提供轻量级进程的系统调用。但是我们用户需要线程的接口,Linux程序员在应用层提供了pthread线程库(应用层——轻量级进程接口进行封装,为用户提供直接线程的接口,这是一个第三方库,几乎所有的Linux平台都是默认自带这个库的,Linux中编写多线程代码,需要使用第三方pthread库)

Linux线程控制

POSIX线程库

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  • 要使用这些函数库,要通过引入头文<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的“-lpthread”选项

创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);
  • 参数

    • thread:返回线程ID

    • attr:设置线程的属性,attr为NULL表示使用默认属性

    • start_routine:是个函数地址,线程启动后要执行的函数

    • arg:传给线程启动函数的参数

  • 返回值:成功返回0;失败返回错误码

  • pthread_create函数:

在这里插入图片描述

  • 先来验证void和void*大小:

在这里插入图片描述

在这里插入图片描述

虽然void有大小,但是void不能定义变量!!!

错误检查:

  • 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
  • pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通 过返回值返回
  • pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误, 建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* startRoutine(void* args)
{
    while (true)
    {
        cout << "线程正在运行..." <<getpid()<< endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, startRoutine, (void*)"thread1");
    cout << "new thread id : " << tid << endl;//线程ID
    while (true)
    {
        cout << "main thread 正在运行..." <<getpid()<< endl;
        sleep(1);
    }
    return 0;
}

  • 我们编译时发现出现的链接报错:

在这里插入图片描述

  • pthread_create说明不是系统调用函数!

  • 编译时要带上**-lpthread**选项:

在这里插入图片描述

在这里插入图片描述

  • 运行结果:

在这里插入图片描述

  • 只有一个进程pid说明是一个进程但有多个执行流

在这里插入图片描述

  • ps -aL查看所有轻量级进程(线程)

在这里插入图片描述

CPU调度的基本单位是线程,所以每一个线程都要有自己的标识符

  • 线程的标识符:

在这里插入图片描述

在这里插入图片描述

  • 用户级执行流 :内核LWP = 1 :1(也有多对1)

  • 不管是kill 18707还是kill 18708,只要其中一个线程被杀死,整个进程都会被杀死。(我们认为将信号发送给线程就是发送给进程,线程是进程的执行分支)所以线程的健壮性很差,只要有一个被干掉了整个都会被干掉。

  • 多个线程执行同一个函数:

在这里插入图片描述

==全局变量,已初始化和未初始化都是和线程共享的!!==线程之间数据共享,为线程之间的通信提供了方便。

  • 打印线程的tid:

在这里插入图片描述

在这里插入图片描述

我们发现我们打印的tid好像和PID或者LWP没有关联

  • 我们换一种打印方式:

在这里插入图片描述

  • 发现打印出来的类似地址,因为PID和LWP是操作系统层面的概念:

在这里插入图片描述

之后我们将会了解到这一串地址指的是什么。

如何给线程传参?
  • 转成无符号类型指针再强转回来:

在这里插入图片描述

在这里插入图片描述

线程ID及进程地址空间布局

栈一定是被线程私有的,共享内存是所有线程共享的

再次谈谈pthread_create中的参数和返回值
  • pthread_create函数中传递的参数可以为结构体:

  • 示例代码:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstdlib>
using namespace std;

class Request
{
public:
    Request(int start,int end,const string &threadname)
    :start_(start),end_(end),threadname_(threadname)
    {}
public:
    int start_;
    int end_;
    string threadname_;
};

class Response
{
public:
    Response(int result,int exitcode):result_(result),exitcode_(exitcode)
    {}
public:
    int result_;
    int exitcode_;
};

void *sumCount(void *args)
{
  
    Request *rq=static_cast<Request*>(args);
  
    Response *rsp = new Response(0,0);
    for(int i=rq->start_;i<=rq->end_;i++)
    {
        rsp->result_ += i;
    }
    delete rq;
    return rsp;
}

int main()
{
    pthread_t tid;
    Request *rq = new Request(1,100,"thread 1");
    pthread_create(&tid,nullptr,sumCount,rq);


    void *ret;
    pthread_join(tid,&ret);
    Response * rsp = static_cast<Response*>(ret);
  
    cout<<"rsp->result"<<rsp->result_<<", exitcode:"<<rsp->exitcode_<<endl;
    delete rsp;
    return 0;
}

在这里插入图片描述

所以线程的参数和返回值,不仅仅可以用来进行传递一般参数,也可以传递对象

所以我们还可以改进上面的代码,将计算的方法放入对象,变成一个成员函数进行调用

通过上面的代码我们发现,堆空间是被线程共享的!!!

  • pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID 不是一回事。

  • 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要 一个数值来唯一表示该线程。

  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID, 属于NPTL线程库(原生线程库)的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。

  • 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:

pthread_t pthread_self(void);

在这里插入图片描述

  • 实验:

在这里插入图片描述

在这里插入图片描述

Linux如何创建一个轻量级进程?
  • clone函数

在这里插入图片描述

 int clone(int (*fn)(void *), void *child_stack,
                 int flags, void *arg, ...
                 /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

fork函数的底层也是它,我们一般不用这个接口,因为参数太多了。

  • 参数:
    • fn:需要执行函数的函数指针
    • child_stack:自定义一个栈
    • flag:创建子进程的时候要不要选择和地址空间实现共享(默认需要)

clone这个接口一般我们是用不了的,所以他被线程库封装了,线程的概念是库给我们维护的,**所以我们在执行多线程代码,库是要被加载到内存的!!!(映射到共享区,或者说动态库),我们的讨论都是基于内存的!!!操作系统没有线程的概念,但是线程的栈,回调函数等线程的属性是由线程库来维护的!!!(主要维护线程的概念,不用维护线程的执行流),线程库注定要维护多个线程属性的集合,线程库要管理这些线程(先描述再组织,每创建一个线程都要创建一个线程库级别的线程控制块(栈,回调方法在哪,线程对应的独立栈在哪,线程的id是什么,线程的LWP指向底层的哪个执行流))**线程库中维护的线程叫做用户级线程(用结构体维护)

pthread到底是什么类型呢?取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质 就是一个进程地址空间上的一个地址。

在这里插入图片描述

pthread.so加载到内存

在这里插入图片描述

可以理解为按照数组的方式维护好tcb,每一个线程库级别的tcb在内存中的起始地址称为线程的tid(因为在共享区,所以我们之前实验出的地址较大)

线程栈

每个线程都有自己独立的调用链,注定了每一个线程都有调用链所对应独立栈帧结构,这个栈帧结构会保存线程在运行时所有的临时变量(压栈变量,传参,返回变量,返回地址,函数自己定义的临时变量等).

  • 其中主线程(真进程)直接用自己地址空间中的栈结构即可
  • 其他线程调用clone,建立的栈在共享区进行维护(具体讲是在pthread库中,tid指向的用户tcb中)!!!

使用共享栈可以减少线程创建时的开销,因为不需要为每个线程都分配独立的栈空间。

同时线程栈不仅仅要实现简单的变量定义,入栈出栈,**实际上每一个执行流的本质就是一条调用链。**这个栈结构在宏观上动态开辟,这个栈结构要完成整个调用链临时变量的开辟和释放。所以每个线程都要有自己的调用链,防止自己不受干扰,所以每个线程必须要有自己的线程栈结构!!!(将来我们也有办法访问这个独立的栈,比如在主线程可以定义一个全局变量,然后指定线程进行赋值即可

  • 我们在主线程为每个线程申请一个堆空间,(用结构体的方式打包线程信息)传递给线程(在线程中要记得释放),并在主线程用vector保存,所以我们可以访问其他线程的数据,但是这是线程的缺点吗?是线程的缺点同时也是线程的特点,因为强调执行流之间的独立性是进程的概念,线程在一个进程内本来就是一家人,所以被互相访问是很正常的

线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。
  • pthread_exit函数

功能:线程终止

原型

​ void pthread_exit(void *value_ptr);

参数
value_ptr:value_ptr不要指向一个局部变量。

返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

  • pthread_cancel函数(不常见)

功能:取消一个执行中的线程
原型
int pthread_cancel(pthread_t thread);

参数
thread:线程ID

返回值:成功返回0;失败返回错误码

在这里插入图片描述

  • 示例:

在这里插入图片描述

在这里插入图片描述

  • 如果一个线程是被取消的,不用自己return,pthread库会让线程退出时设置返回值(-1):

在这里插入图片描述

线程等待 为什么需要线程等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。(防止内存泄漏)

  • 创建新的线程不会复用刚才退出线程的地址空间。

  • 获取线程的退出结果

  • 功能:等待线程结束
  • 原型:
int pthread_join(pthread_t thread, void **value_ptr); 

在这里插入图片描述

  • 参数

​ thread:线程ID

​ value_ptr:它指向一个指针,后者指向线程的返回值

  • 返回值:成功返回0;失败返回错误码

  • 实验:

在这里插入图片描述

在这里插入图片描述

主线程等待其他线程退出的时候,默认是阻塞等待的,只有其他线程执行完了主线程才会退出!!!

  • value_ptr(二级指针进行研究):

在这里插入图片描述

  • 编译不通过:

在这里插入图片描述

原因:类型大小不符合,void*8个字节,int四个字节:

  • 修改:

在这里插入图片描述

  • 运行结果:

在这里插入图片描述

拿到了运行结果。所以我们可以拿到不同的数字作为返回结果,

【问题】:但是!!!void*在不同平台下指针的大小并不相同!!!代码不具有可移植性,怎么修改??

【问题】:为什么我们在这里join的时候不考虑异常呢?

做不到。线程出现异常主线程也会收到影响,所以不用考虑异常问题,因为异常问题是进程考虑的。所以只要考虑正常情况就可以了。

验证:

  • 修改线程执行函数中的代码:

在这里插入图片描述

在这里插入图片描述

我们发现线程调用exit,但是主线程没有执行线程回收之后的打印语句,说明线程的主线程都退出了

所以exit是用来终止进程的,不能用来终止线程!!!任何一个线程调用exit都会导致整个进程退出

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的 终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。

  2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED。

  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。

  4. 如果对thread线程的终止状态不感兴趣,可以传nullptr给value_ ptr参数。

在这里插入图片描述

关于之前介绍过的进程替换:

如果线程中存在程序替换,整个进程都会被替换掉,当然我们可以在fork里面创线程,也可以在线程里面创进程

pthread原生线程库 VS C++11thread库

注:有关C++11thread库将会在之后的博客专门 介绍

  • #include < thread>

在这里插入图片描述

  • 发现编不过:

在这里插入图片描述

在这里插入图片描述

  • 还是必须要带上-lpthread!!

在这里插入图片描述

在这里插入图片描述

经过测试,-std=c++11选项可以不带

因为C++11里面的多线程封装了原生线程库,不管什么语言,都要使用原生线程库,c++里面的库使用了条件编译,所以可以跨平台。

  • 更推荐使用c++的多线程(语言级别的线程库)

【附】:

  • 注意:tid在执行pthread_create函数后才获取了,不能将tid当作参数传递给线程,同时线程内部可以使用pthread_self来获取tid(最好转16进制)

  • 因为都在同一个进程地址空间,所以进程没有秘密,只不过我们要求它们都要有一个独立的栈,但是我们禁止访问线程独立栈里面的数据(独立但不私密)

  • 主线程中的全局变量我们叫做共享资源

  • 线程可以有自己私有的全局变量,使用线程局部存储:__thread (高并发内存池项目有用到(开辟了空间 )) __thread int g_val=100; __thread不是c/c++的,而是编译器编译的一个选项,地址在进程地址空间的共享区,同时__thread只能用来定义内置类型,不能定义自定义类型!每个线程访问这个全局变量时都有对应线程的数据

  • __thread在其他平台的有其他的关键字

在这里插入图片描述

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

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

相关文章

鸿蒙Harmony应用开发—ArkTS-全局UI方法(菜单)

在页面范围内关闭通过bindContextMenu属性绑定的菜单。 说明&#xff1a; 从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 ContextMenu.close 方法描述close(): void可以通过该方法在页面范围内关闭通过bindContextMenu给…

onlyoffice创建excel文档

前提 安装好onlyoffice然后尝试api开发入门 编写代码 <html> <head><meta charset"UTF-8"><meta name"viewport"content"widthdevice-width, user-scalableno, initial-scale1.0, maximum-scale1.0, minimum-scale1.0"&…

3D高斯泼溅的崛起

沉浸式媒体领域正在以前所未有的速度发展&#xff0c;其中 3D 高斯溅射成为一项关键突破。 这项技术在广泛的应用中看起来非常有前景&#xff0c;并且可能会彻底改变我们未来创建数字环境以及与数字环境交互的方式。 在本文中&#xff0c;我们将通过与摄影测量和 NeRF 等前辈进…

数学建模(Topsis python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 案例&#xff1a; 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 标…

低压MOS在无人机上的应用-REASUNOS瑞森半导体

一、前言 无人机的结构由机身、动力系统、飞行控制系统、链路系统、任务载荷等几个方面组成的。 无人机动力系统中的电机&#xff0c;俗称“马达”&#xff0c;是无人机的动力来源&#xff0c;无人机通过改变电机的转速来改变无人机的飞行状态。即改变每个电机的速度&#xf…

灵境矩阵:开启无代码写作新时代,AI智能平台引领创作潮流

灵境矩阵 “灵境杯”智能体创意大赛&#xff0c;瓜分百万超级奖励 在当今数字化快速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正逐渐渗透到我们生活的方方面面。从智能家居到自动驾驶&#xff0c;AI的应用领域不断扩大&#xff0c;而今天&#xff0c;我们…

在基于Android相机预览的CV应用程序中使用 OpenCL

查看&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV4.9.0在Android 开发简介 下一篇&#xff1a;在 MacOS 中安装 本指南旨在帮助您在基于 Android 相机预览的 CV 应用程序中使用 OpenCL ™。教程是为 Android Studio 20…

软考 系统架构设计师系列知识点之云原生架构设计理论与实践(1)

所属章节&#xff1a; 第14章. 云原生架构设计理论与实践 第1节 云原生架构产生背景 云原生&#xff08;Cloud Native&#xff09;是近几年云计算领域炙手可热的话题&#xff0c;云原生技术已成为驱动业务增长的重要引擎。同时&#xff0c;作为新型基础设施的重要支撑技术&…

DMHS同步之MYSQL to MYSQL

一、环境情况 二、源端及目的端安装MySQL&#xff0c;可参考网上资料&#xff0c;此处省略安装过程 三、目的端安装配置unixODBC 1.上传unixODBC-2.3.12.tar.gz包到/opt下 2.解压 cd /opt tar -zvxf unixODBC-2.3.12.tar.gz复制 3.安装 cd unixODBC-2.3.11 ./configure …

使用 React antd 的ProFormSelect组件 搜索查询 多选的写法

使用 React antd 的ProFormSelect组件 搜索查询 多选的写法 需求&#xff1a;需要一个搜索框&#xff0c;可以选择员工&#xff0c;&#xff08;员工人数多无法一次性获取&#xff0c;全部放入options中&#xff09;&#xff0c;所以需要使用搜索功能&#xff0c;而且是可以多…

WebXR实践——利用aframe框架浏览器展示全景图片

一、效果 话不多说&#xff0c;先上效果 二、代码 index.html <!DOCTYPE html> <html><head><meta charset"utf-8"><title>360&deg; Image</title><meta name"description" content"360&deg; Imag…

【QT入门】 Qt槽函数五种常用写法介绍

声明&#xff1a;该专栏为本人学习Qt知识点时候的笔记汇总&#xff0c;希望能给初学的朋友们一点帮助(加油&#xff01;) 往期回顾&#xff1a; 【QT入门】实现一个简单的图片查看软件-CSDN博客 【QT入门】图片查看软件(优化)-CSDN博客 【QT入门】 lambda表达式(函数)详解-CSDN…

odoo扩展导出pdf功能

1. 说明: odoo原生导出功能扩展导出pdf文件功能, 如有额外需求请联系博主 2. 版本说明: odoo版本: odoo15 其他odoo版本未进行测试,如有需要自行测试 3. 地址: 该补丁代码放在github仓库, 地址: https://github.com/YSL-Alpaca/odoo_export_pdf 4. 改补丁依赖于第三方软件wkh…

数学建模(灰色关联度 python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 案例&#xff1a;哪些原因影响结婚率 数据标准化&#xff1a; 灰色关联度系数&#xff1a; 完整代码&#xff1a; 结果&#xff1a; 介绍&#xff1a; 灰色关联度是一种多指标综合评价方法&#xff0c;用于分析和评价不同指标之…

【MySQL】对表的相关操作(DDL)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习计网、mysql和算法 ✈️专栏&#xff1a;MySQL学习 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

软件杯 深度学习 大数据 股票预测系统 - python lstm

文章目录 0 前言1 课题意义1.1 股票预测主流方法 2 什么是LSTM2.1 循环神经网络2.1 LSTM诞生 2 如何用LSTM做股票预测2.1 算法构建流程2.2 部分代码 3 实现效果3.1 数据3.2 预测结果项目运行展示开发环境数据获取 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天…

AI助力生产制造质检,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建工业生产制造场景下的瓷砖瑕疵检测识别分析系统

瓷砖生产环节一般经过原材料混合研磨、脱水、压胚、喷墨印花、淋釉、烧制、抛光&#xff0c;最后进行质量检测和包装。得益于产业自动化的发展&#xff0c;目前生产环节已基本实现无人化。而质量检测环节仍大量依赖人工完成。一般来说&#xff0c;一条产线需要配数名质检工&…

塔楼VR火灾逃生应急安全教育突破了传统模式

城镇化的高速发展&#xff0c;给消防安全带来了严峻的挑战&#xff0c;尤其是人员密集的办公场所&#xff0c;如何预防火灾发生&#xff0c;学习火灾成因&#xff0c;减少火灾发生避免不必要的损失&#xff0c;成为安全应急科普的重中之重。 通过模拟真实的办公场所火灾场景&am…

JVM监控工具

JVM监控工具 jps 查看系统中运行的java进程id PS D:\practise\test> jps 22672 Jps 13688 RemoteMavenServer36 1068 14188 TestApplication PS D:\practise\test> jmap 用来查看进行内存信息&#xff0c;实例个数以及占用内存大小 jmap -histo 进程id PS D:\prac…

Python使用PaddleSpeech实现语音识别(ASR)、语音合成(TTS)

目录 安装 语音识别 补全标点 语音合成 参考 PaddleSpeech是百度飞桨开发的语音工具 安装 注意&#xff0c;PaddleSpeech不支持过高版本的Python&#xff0c;因为在高版本的Python中&#xff0c;飞桨不再提供paddle.fluid API。这里面我用的是Python3.7 需要通过3个pip…