多任务概念

同一时间执行多个任务

多任务优势

最大的好处是充分利用CPU资源,提高程序的执行效率

GIL锁(全局解释锁)

让一个进程中同一个时刻只有一个线程可以被CPU调用,可以解决线程安全问题,有线程锁也有进程锁

Rlock:递归锁

lock:同步锁

多任务的两种表现方式
并发:在一时间段内交替去执行多个任务

如:对于单核cpu处理多任务,操作系统轮流让各个任务交替执行

并行:在一段时间内真正的同时一起执行多个任务

如:对于多核CPU处理多任务,操作系统会给CPU的每个内核安排一个执行的任务,多个内核是真正的一起同时执行多个任务。这里需要注意多核CPU的并行的执行多任务,始终有多个任务一起执行。

程序中实现多任务的方式
进程
进程的概念

进程是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,通俗理解:一个正在运行的程序就是一个进程。例如:正在运行的QQ微信等,它们都是一个进程。

多进程的作用

充分利用CPU资源,提高程序的执行效率,进程与进程之间相互隔离

进程间的数据共享

进程是资源分配的最小单元,每个进程中都维护自己独立的数据,不共享。如果想要它们之间进行共享,则可以借助一些特殊的东西来实现。

多进程完成多任务

进程的三步创建步骤

通过进程类创建进程对象

进程对象=multiprocessing.Process(target=任务名)

进程创建与启动的代码

进程执行带有参数的任务

除了target参数还有另外两种参数

参数args(以元组方式),其中元组的顺序就是任务的参数的顺序

参数kwargs(以字典方式),其中传参字典的key一定要和参数名保持一致

获取进程编号

进程编号的作用:当程序中进程的数量越来越多时,如果没有办法区分主进程和子进程,还有不同的子进程,那么就无法进行有效的进程管理,为了方便管理,实际上每个进程都是有自己编号的。

获取当前进程的编号

os.getpid()

获取当前进程父进程的编号

os.getppid()

常见方法
  1. p.start():当前进程准备就绪,等待CPU调度(工作单元其实是进程中的线程)

  1. p.join():等待当前进程的任务执行完毕后再向下继续进行

  1. p.setFaemon(布尔值):守护进程(必须放在start之前)

  1. p.setFaemon(布尔值):设置为守护进程,主进程执行完毕后,子进程也会自动关闭

  1. p.setFaemon(布尔值):设置为非守护进程,主进程等待子进程,子进程执行完毕后,主进程才会结束(默认)

  1. p.name=“xxx”:进程的名称和设置

  1. multiprocessing.current_process().name:获取当前执行代码的进程名

  1. os.getpid():获取当前进程id

  1. os.getppid():获取当前进程父进程的id

  1. len(threading.enumerate()):获取当前进程中的线程个数

  1. multiprocessing.cpu_count():获取当前cpu个数

进程的注意点

1)主进程会等待所有的子进程执行结束再结束

举例:虽然“主进程执行完成了哦”可能夹在“工作中…”,但是它输出后函数并没有结束,子进程还在执行,待子进程结束后,函数才终止

2)设置守护主进程

举例:加了这行代码后,一旦输出“主进程完成了哦”,就代表函数的终止,子进程会自动销毁,不会在执行

进程池

进程不是开得越多越好,线程池里的线程个数要适量

不建议:无限制的创建进程

建议:使用进程池

线程
线程的概念

线程是程序执行的最小单位,实际上进程只负责分配资源,而利用这些资源执行程序的是线程,也就说进程是线程的容器,一个进程中最少有一个线程来负责执行程序。同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

为什么使用线程

进程是分配资源的最小单位,一旦创建一个进程就会分配一定的资源,就像跟两个人聊QQ就需要打开两个QQ软件一样是比较浪费资源

但是线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源,这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样,实现多任务的同时也节省了资源。

多线程的作用

相比进程,实现多任务的同时也节省了资源

多线程完成多任务

线程的三步创建步骤

通过进程类创建线程对象

线程对象=threading.Thread(target=任务名)

线程创建与启动的代码

线程执行带有参数的任务

除了target参数还有另外两种参数

参数args(以元组方式),其中元组的顺序就是任务的参数的顺序

参数kwargs(以字典方式),其中传参字典的key一定要和参数名保持一致

主线程和子线程的结束顺序

主线程会等待所有的子线程结束后再结束

设置守护主线程:可以让主线程不等待子线程执行完成

线程间的执行程序
  • 线程之间是无序的,是由CPU调度决定某个线程先执行的

  • 获取当前的线程信息

常见方法
  1. t.start():当前线程准备就绪(等待CPU调度,具体时间由CPU来决定)

  1. t.join():等待当前线程的任务执行完毕后再向下继续进行

  1. t.setFaemon(布尔值):守护线程(必须放在start之前)

  1. t.setFaemon(布尔值):设置为守护线程,主线程执行完毕后,子线程也会自动关闭

  1. t.setFaemon(布尔值):设置为非守护线程,主线程等待子线程子线程执行完毕后,主线程才会结束(默认)

  1. t.setName():线程的名称和设置

  1. t.threading.current_thread().getName():获取当前执行代码的线程名

线程池

线程不是开得越多越好,开的多了可能会导致系统的性能更低了。

不建议:无限制的创建线程

建议:使用线程池

进程和线程对比
关系对比

  • 线程是依托在进程里面的,没有进程就没有线程

  • 一个进程默认提供一条线程,进程可以创建多个线程

区别对比
  • 创建进程的资源开销要比创建线程的资源开销大

  • 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位

  • 线程不能够独立运行,必须依托在进程中

优缺点对比
  • 进程优缺点

  • 优点:可以使用多核

  • 缺点:资源开销大

  • 线程优缺点

  • 优点:资源开销小

  • 缺点,不能使用多核

应用场景
  • 如果每个子进程执行需要消耗的时间非常短(执行+1操作等),这不必使用多进程,因为进程的上下文切换(启动关闭)也会耗费资源

  • 使用多进程往往是用来处理CPU密集型(科学计算)的需求,如果是IO密集型(文件读取,爬虫等)则可以使用多线程去处理

  • 想利用计算机的多核优势,让CPU同时处理一些任务,适合用多进程开发(即使资源开销大)

  • 不利用计算机的多核优势,适用于多线程开发