第一部分 进程:
#####################################
1.**多进程**
#####################################
·程序: 是一个指令的集合
- ·进程: 正在执行的程序;或者说:当你运行一个程序,你就启动了一个进程
- 编写完的代码,没有运行时,称为程序,正在运行的代码,称为进程
- 程序是死的(静态的),进程是活的(动态的) - ·操作系统轮流让各个任务交替执行,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样
- ·多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响
- 变量的修改互相之间不影响:
(1)多进程 实际创建举例:
(2)进程启动之后 才能查看进程的name 与 pid
多进程举例:
from multiprocessing import Process
from multiprocessing import Pool
from multiprocessing import Queue
from multiprocessing import Lock
import time
#第一个简单的案例:
def func(name,age):
for i in range(2):
print(f"{name}今年{age}岁了") #简单定义一个函数
time.sleep(1)
if __name__ == "__main__":
p1 = Process(target=func, args=("张三", 12), name="第一个进程") #创建两个小弟。给小弟资源
p2 = Process(target=func, args=("李四", 18), name="第二个进程")
p1.start() #小弟埋头干活
p2.start()
print(p1.name, p2.name, p1.pid, p2.pid) #只有启动之后 才有pid
p1.join() #老大等小弟的回复
p2.join()
要点:
序号 | 说明 |
---|---|
1 | 导入包: from multiprocessing import Process |
2 | 第二点——Process() 通过name给进程起个名字: 如果不起名字 默认就是 Proces_1 process_2 |
3 | 第三点——给函数传递参数、 位置参数用元组形式—— args=(fas,fasf) 如果参数只有一个元素一定要带逗号: p = Process(target=func , args=(num1,) name=“第一个进程”) |
4 | start() 写到一起,表示多个小弟就绪可以干活了。等CPU的调用 |
5 | join() 写在最后,表示等小弟干活结束, 主进程才继续往下走——等小弟活干完了 才往下走,不然一直等进程结束才往下走 |
![]() | |
***进程状态说明: ![]() |
☆ 带join 主程序等待进程结束后继续再往下走 ;
☆ 如果不带join主程序先执行完不等子进程了
2.重写run()方法- start() 会自动调用(用的不多)
进程对象调用start()方法,默认会调用类中的run()函数执行程序
#####################################
2.**进程池 Pool()**
#####################################
进程池
1. ·进程池:用来创建多个进程
2. ·当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process()动态生成多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到
multiprocessing模块提供的Pool
3. ·初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行
理解进程池:
进程池子大小为3,进程池满了之后 后面的任务进入队列开始排队,有进程执行完,后面队列里的进程就是补充进去。pool.close() 表示不再接收新任务。
from multiprocessing import Process
from multiprocessing import Pool
import time
def fun1(name):
s = f"{name}是一个笨鸟,但是只要坚持不懈,肯定可以有所成就,至少可以成为一个合格的程序员"
print(s)
if __name__ == "__main__":
po= Pool(3) ##限制Pool里面能够放置的进程的数量
for i in range(10):
po.apply_async(fun1 , ("thomas",)) #添加进程池,直接调用并运行
po.close()
po.join()
po.apply_async(fun1 , (“thomas”,)) #添加进程池,直接调用并运行
po.close() #关闭进程池 不再接受新的任务
po.join() #主进程阻塞
#####################################
3.**多进程之间互相通信(Queue)**
q.put(数据类型不限) ——往队列里加数据q.get() ——从队列里取数据
#####################################
如下场景: 一个进程负责爬数据,另一个进程负责数据分析,所以需要两个进程之间互相传递数据:
进程之间数据传递,举例如下图:
Queue函数:
Queue.qsize() _队列中消息数量 Queue.empty() 队列为空 返回True 否则返回False
Queue.full() 队列满了返回True,队列没满返回Fasle Queue.put(item,[block=,timeout=])
block默认为True,即队列满了阻塞等待腾出空间再放数据,timeout表示等待时间,如果队列一直满的超过timeout时间报Queue.full异常
Queue.get([block=,timeout=]) 获取数据是否设置超时时间
默认Queue.get(True)
如果是Queue.get(False)-不阻塞拿不到直接报错
要点:
(1) Queue —— FiFo——先入先出——最早进去的最早出来
(2) 放置数据与取数据速率不一致;
比如往池子里放数据的速率很慢, 取数据的速率很快。 取数据的必然要等待数据
再比如放数据的很快, Queue已经阻塞很久,数据还没被取出去。*** 1.1如何设置等待时间 ?
*** 1.2在等待期间是否可以做点别的任务?
from multiprocessing import Queue
from multiprocessing import Process
import time
def func1(name,age,q1): #一级生产商
for i in range(10):
q1.put((name ,i),timeout=20) ##20秒队列还没空间(即退出循环),放置的是一个元组
def func2(q1,q2): #中间过渡
while True:
try:
k,v = q1.get(timeout=20) #20秒获取不到数据报错(即退出循环)
q2.put((k,v))
print("p2 time is:", time.time())
except Exception:
break
def func3(q2): #最终消费者
while True:
try:
k,v = q2.get(timeout=20) #20秒获取不到数据报错(即退出循环)
print("p3获取到的消息是:",k,v)
print("p3 time is:",time.time())
except Exception:
break #退出循环
if __name__ == "__main__":
q1 = Queue(100)
q2 = Queue(20)
p1 = Process(target=func1, args=("messi", 33, q1))
p2 = Process(target = func2, args=(q1, q2))
p3 = Process(target=func3, args=(q2,))
lst= [p1, p2, p3]
for i in lst:
i.start()
for i in lst:
i.join()
if __name__ == "__main__":
q1 = Queue(100)
q2 = Queue(20)
p1 = Process(target=func1, args=("messi", 33, q1))
p2 = Process(target = func2, args=(q1, q2))
p3 = Process(target=func3, args=(q2,"P3") , name ='111') #创建两个P3 与 p3_2 进程从队列里面取数据
p3_2 = Process(target=func3, args=(q2, "P3_2"), name="222")
lst = [p1,p2,p3,p3_2]
p1.start()
p2.start()
p3.start()
p3_2.start()
for i in lst:
i.join()
#####################################
4.**进程池内进程之间互传数据Manager().Queue()**
q.put(数据类型不限) ——往队列里加数据q.get() ——从队列里取数据
#####################################
通过进程池 多个进程之间互相传递数据:
from multiprocessing import Process
from multiprocessing import Pool
from multiprocessing import Queue
from multiprocessing import Lock
import time
def func1(name,age,q1): #一级生产商
for i in range(10):
q1.put((name ,i),timeout=20) #放到队列里面的是一个元组,两个元素
print("此时队列q1的长度为: ",q1.qsize())
#time.sleep(1)
def func2(q1): #从q1 队列里面取值
while True:
try:
k,v = q1.get(timeout=2) #如果超出2秒 取不到值就报错,退出循环
print("获取到的k、v是:",k,v)
time.sleep(1)
except Exception as e:
print(e) ;break
if __name__ == "__main__":
p = Pool()
q1 = Manager().Queue()
#q2 = Manager().Queue()
p.apply_async(func1, args=("james",19, q1))
p.apply_async(func2, args=(q1,))
p.close()
p.join()
补充知识:
☆☆☆☆☆☆☆☆☆☆重要知识☆☆☆☆☆☆☆☆☆☆
在创建子进程的前面要加上
__name__ == “__main__” (只有windows系统要加) 子进程默认会导入主进程代码(默认执行一遍),所以会导致递归创建子进程