• python(48): 进程,线程 ,协程


    区别

    进程拥有代码和打开的文件资源、数据资源、独立的内存空间。

    线程:线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。线程拥有自己的栈空间。

    对操作系统来说,线程是最小的执行单元,进程是最小的资源管理单元。

    进程--资源分配最小单位,线程,资源调度最小单位

    协程:英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。

    最重要的是,协程不是被操作系统内核所管理,而完全是由程序(用户/库)所控制(也就是在用户态执行)。

    这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

    资源对比

    进程:拥有自己独立的堆和栈,既不共享堆,也不共享栈,进程由操作系统调度;

    线程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系统调度;

    协程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,协程由程序员在协程的代码里显示调度

     

    进程

    进程与资源

    学习 Linux 时,经常可以看到两个词:User space(用户空间)和 Kernel space(内核空间)。

    简单说,Kernel space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

    地址空间

    4GB 的进程虚拟地址空间被分成两部分:用户空间和内核空间

    用户空间

    用户空间按照访问属性一致的地址空间存放在一起的原则,划分成 5个不同的内存区域。 访问属性指的是“可读、可写、可执行等 。

    • 代码段

    代码段是用来存放可执行文件的操作指令,可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,它是不可写的。

    • 数据段

    数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。

    • BSS段

    BSS段包含了程序中未初始化的全局变量,在内存中 bss 段全部置零。

    • 堆 heap

    堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

    • 栈 stack

    栈是用户存放程序临时创建的局部变量,也就是函数中定义的变量(但不包括 static 声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    上述几种内存区域中数据段、BSS 段、堆通常是被连续存储在内存中,在位置上是连续的,而代码段和栈往往会被独立存放。堆和栈两个区域在 i386 体系结构中栈向下扩展、堆向上扩展,相对而生。

    你也可以再 linux 下用size 命令查看编译后程序的各个内存区域大小:

    1. [lemon ~]# size /usr/local/sbin/sshd
    2. text data bss dec hex filename
    3. 1924532 12412 426896 2363840 2411c0 /usr/local/sbin/sshd

    内核空间

    在 x86 32 位系统里,Linux 内核地址空间是指虚拟地址从 0xC0000000 开始到 0xFFFFFFFF 为止的高端内存地址空间,总计 1G 的容量, 包括了内核镜像、物理页面表、驱动程序等运行在内核空间 。

    线程

    线程是操作操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中的实际运作单位,一个进程内可以包含多个线程,线程是资源调度的最小单位。

    线程资源和开销

    同一进程中的多条线程共享该进程中的全部系统资源,如虚拟地址空间,文件描述符文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈、寄存器环境、线程本地存储等信息。

    线程创建的开销主要是线程堆栈的建立,分配内存的开销。这些开销并不大,最大的开销发生在线程上下文切换的时候。

    特点:

    多线程并发时占用cpu时间由系统调度,所以多线程编程时需要考虑线程安全。

    协程 

    类比一个进程可以拥有多个线程,一个线程也可以拥有多个协程,因此协程又称微线程和纤程。

    Python 和 Go 从语言层面提供了对协程很好的支持 

    调度开销

    线程是被内核所调度,线程被调度切换到另一个线程上下文的时候,需要保存一个用户线程的状态到内存,恢复另一个线程状态到寄存器,然后更新调度器的数据结构,这几步操作设计用户态到内核态转换,开销比较多。

    协程的调度完全由用户控制,协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作用户空间栈,完全没有内核切换的开销。

    python协程的几个概念:

    event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足条件发生的时候,就会调用对应的处理方法。

    coroutine:中文翻译叫协程,在 Python 中常指代为协程对象类型,我们可以将协程对象注册到时间循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。

    task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。

    future代表将来执行或没有执行的任务的结果,实际上和 task 没有本质区别。

    async关键字async 定义一个协程;

    await 关键字用来挂起阻塞方法的执行。

    注意事项:在特殊函数内部不可以出现不支持异步模块相关的代码。(例:time,request)

    协程优点缺点

    优点

    1.无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)
    2.无需原子操作锁定及同步的开销

    缺点

    1.无法利用多核

    2.进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

    适用场景:爬虫程序,IO密集

    爬虫协程:Python爬虫进阶 | 异步协程 - PythonGirl - 博客园

    爬虫场景多协程与多线程比较:

    多线程情况时,发起请求,此线程会等待响应,系统切换其他线程执行

    多协程时,一个协程序发起请求后,代码级别切换执行另一个协程,能充分利用线程工作,并且没有系统上下文切换开销。

    问题:

    为什么线程是操作系统调度的最小单元?
    一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    参考

    干货 | 进程、线程、协程 10 张图讲明白了! - 知乎

    线程,进程,协程详细解释_幽雨雨幽的博客-CSDN博客

    python 协程与异步IO - 刘江的python教程

    https://www.jianshu.com/p/7c851145ee4c

    理解Python的协程(Coroutine) - 掘金

  • 相关阅读:
    AI究竟能提升多少效率?哈佛已揭秘量化结果
    云原生之旅 - 3)Terraform - Create and Maintain Infrastructure as Code
    CSS-表格属性(1)
    将base64格式的图片画到canvas上(js和vue两种)
    git基础命令(一)
    Linux操作系统 实现一个进程管理子系统
    dp好题集锦
    jvm工作中的总结
    【Java】day01 - Java基础语法
    单机模式与集群模式的区别?
  • 原文地址:https://blog.csdn.net/qq_37674086/article/details/126433311