Python进阶-多线程.多进程.进程池.多进程通信.多线程通信



0.144 2019.06.11 11:33:38



哈哈哈,标题有点长,但我们依然由浅入深。

# 通常情况下需要我们的程序同时进行多个任务,并发运行。由于cpu的执行效率非常高,时间片非常短,在各个任务之间快速的切换,给人的感觉就是多个任务在同时进行,并发运行。

# 进程和进程之间的区别

# 1、线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位

# 2、一个进程是由一个或多个线程组成,线程是一个进程中代码的不同执行路线

# 3、进程之间相互独立,但同一个进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其他进程是不可见的

# 4、调度和切换:线程上下文切换比进程上下文切换要快得多

# 总之,进程与线程都是一种抽象的概念,线程是一种比进程更小的抽象,线程和进程都可用于实现并发

# 可以这样想象为,一个APP在手机中运行,这一个APP是一个进程,对于这个APP而言,我们在内部要加载很多页面,每一个页面都是一个线程,一个手机中又可以有很多APP,即有很多进程。

单线程

先看个例子:单线程顺序执行。





python 多进程通信 返回结果_子进程


单线程顺序执行,执行完听音乐才能看电影


多线程

# 在Python的标准库中有两个模块,thread 和threading,thread 是低级模块,threading是高级模块,对thread进行了封装。 一般情况下,我们只使用threading这个模块。


python 多进程通信 返回结果_子进程_02


a创建线程实例,b开启这个线程


主线程

上述例子我们创建了两个新的线程,其实当我们运行这个Python程序的时候,存在一个主线程。

看个例子更好说明。



主线程和我们新实例化的线程是相互独立的


# 出现这种情况是因为: 是在python程序的主线程中,t1,t2 是我们自己创建的新线程。不是一个线程,大家都是同步执行的。要想达到我们在音乐和电影线程都结束,再执行主线程那么我们需要阻塞主线程。


python 多进程通信 返回结果_多线程_03


join(time)


这是想要主线程等待其他线程执行完再执行,通过join()阻塞主线程。

但是如果我没想执行完主线程,就将其他的线程结束,这时候就需要setDaemon(boolean)方法守护线程。


python 多进程通信 返回结果_主线程_04


线程守护


这里就可以讲讲为什么一般情况下,我们只使用threading这个模块?

# thread不支持守护线程,当主线程退出时,所有的子线程不管他们是否还在工作,都会被强行退出, threading模块支持守护线程。

看个不加锁的例子


python 多进程通信 返回结果_python 多进程通信 返回结果_05


单线程顺序执行


python 多进程通信 返回结果_多线程_06


多线程


在多线程对同一个全局变量进行操作的时候,t1在未执行完对balance 的操作 t2抢占执行,t2未执行完,t1又开始抢占资源。因为两个线程公用一个资源,所以给改差皮了。

这时候我们需要加一个锁。



注意最后释放锁资源


# 在Python中不建议多线程,建议多进程。

在Python中,无论你写多少个线程,在唯一时间点上只有一个线程在运行,其他的都在等待

# 即保证同一时刻只有一个线程对共享资源进行存取。(GIL全局解释锁,Python 大锁。

单进程



学语文的时候不学数学,一个一个学


多进程

# multiprocessing库的出现很大程度上是为了弥补threading库因为GIL低效的缺陷。 它完整的复制了一套threading所提供的接口方便迁移。唯一的不同就是它使用了多进程而不是多线程。每个进程有自己的独立的GIL,完全并行,无GIL的限制(进程中包括线程),可充分利用多cpu多核的环境,因此也不会出现进程之间的GIL争抢。


python 多进程通信 返回结果_主线程_07


现在拥有两个大脑,同时学习数学语文



python 多进程通信 返回结果_多线程_08


对于公共资源保证安全性


两个进程虽然同时开启,但是进程1对公共资源修改完成,进程2再开始修改。

进程池

先介绍两个概念:
# 同步执行:一个进程在执行任务时,另一个进程必须等待执行完毕,才能继续执行, 加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。

# 异步执行:一个进程在执行任务时,另一个进程无需等待其执行完毕就可以执行, 当有消息返回时,系统会提醒后者进行处理,这样会很好的提高运行效率。


python 多进程通信 返回结果_python 多进程通信 返回结果_09


线程池可以设置大小,若不设置通常默认4个


通过执行结果可以看出,每次执行2个子进程,当有一个子进程执行完毕,会有一个新的子进程进入进程池开始执行。极大的优化效率。

# Pool可以提供指定数量的进程,供用户调用。当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行它。


python 多进程通信 返回结果_子进程_10


父进程被阻塞,即使是容量为2的进程池也只能容纳一个子进程执行


跨进程通信

两种方式:队列( Queue );管道 (Pipe)


python 多进程通信 返回结果_python 多进程通信 返回结果_11


队列( Queue )


注意:

terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁。


python 多进程通信 返回结果_python 多进程通信 返回结果_12


管道 (Pipe)


线程间通信

# 通过队列 queue.Queue()


python 多进程通信 返回结果_python 多进程通信 返回结果_13


线程间通信


# 如果生产者的速度 远远大于,消费者的速度 ,生产者只会多生产10个,之后,消费者每消费一个,生产者生产一个。如果生产者的速度小于消费者,消费者线程阻塞,等待生产者生产后,消费者再消费。