Python多线程编程学习笔记


文章目录


写在前面

1. 多进程

  1. 多任务
  1. 多任务的优势:充分利用CPU资源,提高程序的执行效率
  2. 多任务是指在同一时间内执行多个任务
  3. 多任务的表现形式
  • 并发:在一段时间内交替去执行多个任务
  • 并行:在一段时间内真正的同时一起执行多个任务
  1. 进程
  1. 进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位
  2. 一个程序至少有一个进程,程序是静态的,进程是动态的
  1. 多进程完成多任务
  1. p = multiprocessing.Process(target, name, group)
  2. p.start()启动进程
  3. 测试
    from multiprocessing import Process import time # 唱歌 def sing(): for i in range(3): print('唱歌...') time.sleep(0.5) # 跳舞 def dance(): for i in range(3): print('跳舞...') time.sleep(0.5) if __name__ == '__main__': p1 = Process(target=sing) p2 = Process(target=dance) p1.start() p2.start()
  1. 进程执行带有参数的任务
  1. args参数的使用
  2. kwargs参数的使用
    from multiprocessing import Process import time import os # 唱歌 def sing(num): print('唱歌进程的pid:', os.getpid()) print('唱歌父进程的pid:', os.getppid()) for i in range(num): print('唱歌...') time.sleep(0.5) # 跳舞 def dance(num2): print('跳舞进程的pid:', os.getpid()) print('跳舞父进程的pid:', os.getppid()) for i in range(num2): print('跳舞...') time.sleep(0.5) if __name__ == '__main__': print('主进程的pid:', os.getpid()) p1 = Process(target=sing, args=(5,)) p2 = Process(target=dance, kwargs={'num2': 2}) p1.start() p2.start()
  1. 获取进程的编号
  1. 获取当前进程的编号:​​os.getpid()​
  2. 获取当前父进程的编号:​​os.getppid()​
  1. 进程的注意点
  1. 主进程会等待所有的子进程执行结束后再结束
  2. 设置守护主进程:主进程不会等待子进程结束就会结束
    子进程对象.daemon = True

2. 多线程

  1. 为什么要使用多线程:
    进程是分配资源的最小单位,一旦创建一个进程就会分配一定的资源。线程是程序执行的最小单位,一个进程中最少有一个线程来负责执行程序。线程自己不拥有系统资源,它可以与同属一个进程的其他线程共享进程所拥有的全部资源。
  2. 多线程完成多任务:跟进程一毛一样,只不过模块不一样。
    from threading import Thread
  3. 线程执行带有参数的任务:跟进程一毛一样
  4. 主线程和子线程的结束顺序:也跟进程一毛一样只不过设置守护线程的方法有两种:
  1. 在创建的时候,传递daemon参数
  2. 创建后,设置daemon
  1. 线程间的执行顺序
  1. 线程之间的执行是无序的,由CPU调度
  2. 获取当前的线程信息:​​threading.current_thread()​
  1. 进程和线程对比
  1. 关系对比:
  1. 线程是依附在进程里面的,没有进程就没有线程
  2. 一个进程默认提供一条线程,进程可以创建多个线程
  1. 区别对比:
  1. 创建进程的资源开销要比创建线程的资源开销要大
  2. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
  3. 线程不能够独立执行,必须依存在进程中
  1. 优缺点对比:
  1. 进程优缺点:
    优点:可以用多核
    缺点:资源开销大
  2. 线程优缺点:
    优点:资源开销小
    缺点:不能使用多核

3. 补充

  1. join:等待该线程执行完之后,才接着运行主线程后面的语句。
  2. 线程的返回是不能用return的,也就是说多线程的时候,不能通过return获取函数的返回值,需要传入一个记录返回值的列表,然后将函数的结果放入列表中。
  3. GIL 限制了 Python 多线程的性能不会像我们预期的那样。GIL:全局解释器锁,Global Interpreter Lock。GIL参考链接,​​参考链接2​
  • 我们该如何解决GIL锁的问题呢?
  1. 更换cpython为jpython(不建议)
  2. 使用多进程完成多线程的任务
  3. 在使用多线程可以使用c语言去实现
  • 什么时候会释放GIL?
  1. 遇到像 i/o操作这种 会有时间空闲情况 造成cpu闲置的情况会释放Gil
  2. 会有一个专门ticks进行计数 一旦ticks数值达到100 这个时候释放Gil锁 线程之间开始竞争Gil锁(说明: ticks这个数值可以进行设置来延长或者缩减获得Gil锁的线程使用cpu的时间)
  • 互斥锁和Gil锁的关系?
  1. Gil锁 : 保证同一时刻只有一个线程能使用到cpu
  2. 互斥锁 : 多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱
  1. 互斥锁​​threading.Lock()​​,执行时需要​​lock.acquire()​​,结束后需要​​lock.release()​​,这样有锁的线程才能操作。