zoukankan      html  css  js  c++  java
  • 进程线程协程

    01并发和并行

    >>>>多任务 

    多任务可以通过多进程 多线程 协程来实现

    CPU和多任务的关系图片

      

    >>>>并发和并行

    • 并发指的是任务数多于CPU核数,通过操作系统的各种任务调度算法,实现用多个任务一起执行。

      (实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)

    • 并行指的是任务数小于等于CPU核数,即任务真的是一起执行的。

    >>>>同步和异步

    • 同步是指线程在访问某一资源时,获得了资源的返回结果后才会执行其他操作(先做某件事,再做某件事)。

      可以理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B执行,B执行后将结果给A,A再继续操作。

    • 异步与同步相对,是指线程在访问某一资源时,无论是否取得返回结果,都进行下一步操作,当有了资源返回结果时,系统自会通知线程。

    02线程

    >>>>简述python线程的缺陷以及适用场景

    因为有GIL锁的存在,python中的多线程在同一时间没办法同时执行(即没办法实现并行)

    适用场景:涉及到网络磁盘IO的任务都是IO密集型任务,这类任务的特点是cpu消耗很少

    任务的大部分时间都在等到IO操作完成(因为IO的速度要远远低于cpu和内存的速度)

    >>>>多线程创建方式一

    Thread类可以用来创建线程对象

    target:指定线程执行的任务(一般是任务函数)

    args kwargs:接受任务函数的参数 args=("aa",) kwargs={"name":zn} name:指定线程的名字

    >>>>多线程创建方式二 继承类来创建线程

    通过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,可以通过类来封装,新建一个类,只要继承threading.Thread就可以了,然后重写run方法

    说明:threading.Thread类的run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。

    而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python解释器进行调度。

    当该线程获得执行的机会时,就会调用run方法执行线程

    一个线程类只能写一个run方法,如果有多个任务要执行,则要创建多个线程类

    >>>>多线程共享全局变量

    python中的多线程可以共享全局变量
    缺点:但是会出现资源竞争,导致全局变量数据不准确。比如在计算时赋值还未完成,线程已经切换。

    操作系统如何切换py中的线程?
    GIL全局解释器锁 同一时间只会执行一个线程,如果线程要执行必须要先获取全局解释器锁
    1.遇到耗时等待 例如time.sleep(1)会自动释放GIL锁
    2.当线程执行时间达到一定的阈值,会自动释放GIL锁
    所以线程没有办法并行只能并发

    >>>>互斥锁解决资源竞争问题

    互斥锁为资源引入一个状态:锁定/非锁定

    某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定,其他线程不能更改直到该线程释放资源,将资源的状态变成非锁定,其他的线程才能再次锁定该资源。

    互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的准确性
    threading模块定义了Lock类,可以方便的处理锁定:

    注意:如果这个锁之前是没有上锁的。那么acquire不会堵塞

    如果在调用acquire这个锁上锁之前 他已经被其他线程上了锁那么此时acquire堵塞,直到这个锁被解锁为止

    >>>>死锁

    在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁

    尽管死锁很少发生,但一旦发生就会造成应用的停止响应

    使用线程多的时候一定要避免出现死锁

    03队列

    使用队列来实现线程间数据的同步,可以确保数据的安全

    queue模块只能在一个进程的多个线程中使用三种队列

    >>>>先入先出

     

    >>>>后入先出

    >>>>优先级队列

    队列中的数据为元祖类型,元祖的第一个元素表示数据的优先级,优先级越小的越先出来

    关于优先级,尽量使用数值,如果全是字符串,会按ASCII码进行排序

    quene模块一个队列只能在一个进程中使用 一个进程中多个线程使用

    >>>>队列在多线程中的应用

    04 进程

    进程是操作系统资源分配的基本单位,一个进程中可以有多个线程
    线程:线程是操作系统任务调度的基本单位

    多个进程可以同时进行
    每个进程之间资源是独立的

    >>>>进程和线程对比

    功能
    进程 能够完成多任务,比如在一台电脑上能够同时运行多个软件
    线程 能够完成多任务,比如一个qq中的多个聊天窗口

    定义的不同

    进程是系统进行资源分配和调度的一个独立单位

    线程是进程的一个实体,是CPU调度和任务分派的基本单位,它比进程更小的能独立运行的基本单位。

    线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,但是它可与同属一个进程的其他线程共享进程所拥有的全部资源

    python中同一个进程中的线程 是没办法并行的(GIL),进程是可以并行的,不同进程中的线程也是可以并行的

     

     

     >>>>多进程不可共享全局变量

    Python 多进程默认不能共享全局变量,因为进程的资源是独立的

    主进程与子进程是并发执行的,进程之间默认是不能共享全局变量的(子进程不能改变主进程中全局变量的值)

    >>>>多进程之间通信

    进程之间通信:使用队列

    multiprocessing.Queue:可以多个进程之间共用(通用)跨进程通讯 队列要在主进程中创建 当成参数传给子进程

    queue.Queue模块只能在一个进程中使用 一个进程中多个线程使用

    >>>>通过继承自定义进程类

    >>>>获取进程id

    >>>>进程和队列使用实例

    05 协程

    协程又称微线程,是python中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。

    为啥说他是一个执行单元,因为它自带CPU上下文,这样只要在合适的gr时机,我们可以把一个协程切换到另一个协程。只要这个过程中保存或恢复CPU上下文那么程序还是可以运行的。

    通俗的描述协程是线程中的一个特殊的函数,这个函数执行的时候,可以在某个地方暂停,并且可以重新在暂停处继续运行。

    协程在进行切换的时候,只需要保存当前协程函数中的一些临时变量信息,然后切换到另外一个函数中执行,并且切换的次数以及什么时候再切换回原来的函数,都由开发者自己决定。

    协程切换的时候既不涉及到资源切换们也不涉及到操作系统的调度,而是在同一个程序中切换不同的函数执行。

    所以协程占用的资源非常少,切换的时候几乎不耗费什么资源,一秒钟切换个上万次系统都扛得住。

    所以说协程与进、 线程相比不是一个维度的概念。

    >>>>原生的协程实现多任务 一般不用(了解)

    协程函数的定义和调用

    async 加在def前面定义协程函数

    await:只能写在协程函数中 await后面必须是一个可等待对象,协程任务 asyncio.sleep()

    >>>>greenlet模块实现多任务 一般不用(了解)

    为了更好的使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变得简单,只能手动去切换

    >>>>gevent模块 使用协程要使用的模块

    gevent模块又对greenlet进行了一层封装

    当程序遇到io耗时等待的时候 会自动进行切换

    gevent中默认是遇到gevent.sleep()会进行切换

    如果让gevent遇到io自动切换,节省运行时间 需要在程序的导包处加一个monkey补丁,注意:只能在单线程中用,不支持多线程,加了monkey补丁,遇到time.sleep也会自动切换

    from gevent import monkey monkey.patch_all() 两行代码要在文件的最上层引入

    线程的切换:耗时io操作 网络磁盘 input output 协程切换:遇到io操作


    06进程线程协程对比

    进程、线程、协程对比:

      • 进程是资源分配的单位;
        线程是操作系统调度的单位;
        协程又名微线程,存在于线程之中

      • 进程切换需要的资源最大效率很低;
        线程切换需要的资源一般效率一般当然在不考虑G I L锁的情况下;
        协程切换任务资源很小,效率高

      • 多进程多线程。根据CPU核数不一样,只有多进程能实现并行,但是协程是在一个线程中,所以是并发

      • 注意点Python中的线程由于GIL锁的存在,并不能够实现并行,要充分利用多核C P U还是需要使用进程来做。


    07进程池

    当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程。

    但如果是成百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的pool方法。

    初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行请求。

    但是如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。

    >>>>进程池之间的队列 

    进程池和进程池之间进行通讯使用进程池里的队列
    进程池的Queue 如果要使用Pool创建进程,就需要使用multiprocessing.Manager().Queue(),而不是multiprocessing.Queue(),否则会报错。

    >>>>concurrent.futures实现进程池 线程池

  • 相关阅读:
    字符串子串
    素数
    DP4056替代LTC4056/TP4056锂电池充电保护芯片_1000mA
    DP4054替代LTC405x系列500mA锂电池充电芯片
    Ci522替代MFRC522手机NFC开锁应用13.56Mhz读写器芯片
    node补充dom环境
    springCloud组件详细解析
    23种设计模式——代理模式
    redis实现接口限流
    JVM常用调优参数以及linux服务器启动项目命令jvm参数调优案例
  • 原文地址:https://www.cnblogs.com/pywen/p/14924082.html
Copyright © 2011-2022 走看看