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:保存状态
      • 并发:切换
  • 相关阅读:
    iOS 面试题搜集
    iOS 常用第三方类库、完整APP示例
    iOS 键盘遮挡输入 解决办法
    iOS UIColor RGB HEX
    iOS APP性能优化
    iOS Swift 数组 交换元素的两种方法
    iOS CoreData primitive accessor
    iOS Start developing ios apps (OC) pdf
    iOS 传值方式
    iOS IB_DESIGNABLE IBInspectable @IBDesignable @IBInspectable 加速UI开发
  • 原文地址:https://www.cnblogs.com/maqiaobin/p/11732905.html
Copyright © 2011-2022 走看看