zoukankan      html  css  js  c++  java
  • day_31

    昨日回顾

    GIL全局解释锁

    python解释器

    1. Cpython(C语言编写)
    2. Jpython(Java编写)
    3. Ppython(Python编写)

    GIL全局解释锁

    基于CPython来研究全局解释器锁,因为CPython的内存线程不是安全的

    1. GIL本质上是一个互斥锁
    2. GIL是为了阻止同一个进程内多个线程同时执行(并行)
    3. GIL的存在就是为了保证线程安全

    注意:多个线程过来执行,一旦遇到IO操作,就会立马释放GIL解释器锁,交给下一个先进来的线程

    多线程的作用

    计算密集型程序

    • 在单核情况下,若一个任务需要10s
      • 开启进程,消耗资源大,执行4个进程需要40s
      • 开启线程,消耗资源小,执行4个线程需要40s
    • 在多核情况下,若一个任务需要10s
      • 开启进程,并行执行,效率较高,执行4个进程需要10s
      • 开启线程,并发执行,效率较低,执行4个线程需要40s

    IO密集型程序

    • 在单核情况下,若一个任务需要10s
      • 开启进程,消耗资源大,执行4个进程需要40s
      • 开启线程,消耗资源小,执行4个线程需要40s
    • 在多核情况下,若一个任务需要10s
      • 开启进程,并行执行,效率小于线程,因为遇到IO会立即切换CPU执行权限,执行4个进程需要10s = 开启进程的额外时间
      • 开启线程,并发执行,效率高于进程,执行4个线程需要40s

    死锁现象

    死锁是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程 ,解决方式就是递归锁

    递归锁

    递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

    这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

    信号量

    信号量Semaphore管理一个内置的计数器,
    每当调用acquire()时内置计数器-1;
    调用release() 时内置计数器+1;
    计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()

    注意: 与进程池是完全不同的概念,进程池Pool(4),最大只能产生4个进程,而且从头到尾都只是这四个进程,不会产生新的,而信号量是产生一堆线程/进程

    线程队列

    线程队列

    queue队列:使用import queue,用法与进程Queue一样

    先进先出

    FIFO队列: 先进先出 class queue.Queue(maxsize=0)

    后进先出

    LIFO队列: 后进先出 class queue.LifoQueue(maxsize=0 )

    优先级队列

    优先级队列: 根据参数内,数字的大小进行分级,数字值越小,优先级越高class queue.PriorityQueue(maxsize=0)

    put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高

    今日内容

    Event事件

    Event事件的作用

    1. 用来控制线程的执行
    2. 由一些线程去控制另一些线程

    进程池与线程池

    1. 什么是进程池与线程池

      进程池与线程池是用来控制当前程序允许创建(进程/线程)的数量

    2. 进程池与线程池的作用

      保证在硬件允许的范围内创建(进程/线程)的数量

    3. 如何使用

      from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
      import time
      
      # ProcessPoolExecutor(5)  # 5代表只能开启5个进程
      # ProcessPoolExecutor()  # 默认以CPU的个数限制进程数
      
      pool = ThreadPoolExecutor(5)  # 5代表只能开启5个线程 -5 +1 -1 +1 -1
      # ThreadPoolExecutor()  # 默认以CPU个数 * 5 限制线程数
      
      # t = Tread()  # 异步提交
      # t.start(0)
      
      
      
      # pool.submit('传函数地址')  # 异步提交任务
      # def task():
      #     print('线程任务开始了...')
      #     time.sleep(1)
      #     print('线程任务结束了...')
      #
      #
      # for line in range(5):
      #     pool.submit(task)
      
      
      # 异步提交任务
      # pool.submit('传函数地址').add_done_callback('回调函数地址')
      def task(res):
          # res == 1
          print('线程任务开始了...')
          time.sleep(1)
          print('线程任务结束了...')
          return 123
      
      
      # 回调函数
      def call_back(res):
          print(type(res))
          # 注意: 赋值操作不要与接收的res同名
          res2 = res.result()
          print(res2)
      
      
      for line in range(5):
          pool.submit(task, 1).add_done_callback(call_back)
      
      
      # 会让所有线程池的任务结束后,才往下执行代码
      # pool.shutdown()
      
      print('hello')
      

    协程

    1. 进程:资源单位
    2. 线程:执行单位
    3. 协程:在单线程下实现并发

    注意:协程不是操作系统资源,他是程序起的名字,为了让单线程能实现并发

    协程的目的

    • 操作系统:

      多到技术,切换+保存状态

      • 遇到IO
      • CPU执行时间过长
    • 协程

      通过手动模拟操作系统“多到技术”,实现 切换+保存状态

      • 手动实现 遇到IO切换,欺骗操作系统误以为没有IO操作
      • 单线程 计算密集型,来回切换+保存状态时,反而效率更低

      优点:

      在IO密集型的情况下,会提高效率

      缺点:

      若在计算密集型的情况下,来回切换,反而效率更低

      如何实现协程:切换+保存状态

      • yield:保存状态
      • 并发:切换
  • 相关阅读:
    第07组 Beta冲刺(1/5)
    软工实践个人总结
    SDN大作业
    第08组 Beta版本演示
    2019 SDN上机第7次作业
    第08组 Beta冲刺(5/5)
    第08组 Beta冲刺(4/5)
    第08组 Beta冲刺(3/5)
    第08组 Beta冲刺(2/5)
    2019 SDN上机第6次作业
  • 原文地址:https://www.cnblogs.com/maqiaobin/p/11732905.html
Copyright © 2011-2022 走看看