多进程的定义
提高效率(增加并发数)
进程是程序一次动态的执行过程,包括代码加载,执行,执行完毕退出阶段
进程是系统资源分配的独立单位(最小单位)
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
多进程的特性
并发性:任何进程在操作系统中可以同时运行
独立性:资源不共享
异步性:进程和进程之间相互制约,进程运行有间断性
os.fork()
Unix/Linux操作系统提供了一个os.fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用os.getppid()就可以拿到父进程的ID。
实例:
import osimport time
def child():
print('我是子进程,id是:%d,父进程id是:%d' % (os.getpid(),os.getppid()))
time.sleep(30) #当主进程结束 子进程还在运行时 子进程的ppid为 0
os._exit(0) #结束子进程
def parent():
while True:
newpid = os.fork() #Unix/Linux操作系统才有的方法
if newpid == 0: # 子进程
child()
else:
print('我的父进程id是:%d ------ 子进程id是:%d' % (os.getpid(),newpid))
if input() == 'q':
break
parent()
运行结果为:
我是子进程,id是:10026,父进程id是:10025我的父进程id是:10025 ------ 子进程id是:10026
multiprocessing
由于python是跨平台的语言,但是windows上又没有os.fork()方法,所以multiprocessing模块就是跨平台版本的多进程模块
multiprocessing提供了一个类Process来创建子进程对象,(帽提剖赛应)
from multiprocessing import Processimport os
def chilid(name):
print('我是子进程%s我的进程id是%d'%(name,os.getpid()))
print('我的父级进程是%s'%os.getppid())
if __name__ == '__main__': #必须加if判断
print('我是父级进程%s'%os.getpid())
p = Process(target=chilid,args=("test",)) #创建子进程
p.start()#开启子进程
p.join() #等待所有进程运行完毕
target : 子进程运行的函数
args:子进程函数需要携带的参数,需元组类型传入
start( ):让子进程开启运行
join( ):方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
运行结果为:
我是父级进程14556我是子进程test我的进程id是15744
我的父级进程是14556
进程间的资源共享
因为进程与进程之间资源不共享,但我们process之间又要通信,所以Python包装了底层的机制,提供了Queue、manage等多种方式来交换数据。
我们以multiprocessing中的manage方法为例,来实现多个进程向一个资源里添加数据
import multiprocessingfrom multiprocessing import Manager
#子进程执行函数
def foo(a,i):
a.append(i)
if __name__ == '__main__':
#实例化资源共享对象
m = Manager()
a = m.list() #使用共享对象创建列表list 也可以创建共享Queue,dict等
p_list = []#进程列表
for i in range(10):
#创建一个子进程对象
p = multiprocessing.Process(target=foo,args=(a,i))
#开始执行子进程
p.start()
p_list.append(p)#把所有进程加入进程列表
for p in p_list:
p.join() #等待所有进程执行完毕再继续往下执行
print(a)
print("执行完毕")
执行结果为:
[2, 1, 3, 0, 4, 6, 7, 5, 8, 9]执行完毕
常用函数:
q.qsize( )返回当前队列包含的消息数量
q.full( )表示当前队列是否已经满了 True--满了 False--未满
q.empty( )表示当前队列是否为空 True--空 False--不为空
q.put( )(消息,block=True,timeout=None)
q.get(block=True,timeout=None)
block(默认True) -- 阻塞(停在put状态)直到put进去为止
timeout(默认None) --一直等 如果设置timeout则会等待N秒 然后强制put
q.put_nowait( ) --相当于q.put(消息,False)
q.get_nowait( ) --相当于q.get(False)
pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
Pool 可以提供指定数量的进程供用户使用,默认是 CPU 核数。当有新的请求提交到 Poll 的时候,如果池子没有满,会创建一个进程来执行,否则就会让该请求等待。
- Pool 对象调用 join 方法会等待所有的子进程执行完毕
- 调用 join ( )方法之前,必须调用 close( )
- 调用 close 之后就不能继续添加新的 Process 了
实例
from multiprocessing import Poolimport requests
def tasl(url):
response = requests.get(url)
print(response)
if __name__ == '__main__':
#使用Pool创建12个进程
pool = Pool(12)
base_url = 'https://hr.tencent.com/position.php?start=%d'
for i in range(0,3760+1,10):
fullurl=base_url % i
#使用apply_async 方法用来异步执行进程,允许多个进程同时进入池子。
pool.apply_async(func=tasl,args=(fullurl,))
#关闭进程池 进程池中不能再创造新的进程
pool.close()
#等待进程池中所有进程运行完毕
pool.join()
总结:
在Unix/Linux下,可以使用os.fork( )电调用实现多进程。
想实现跨平台需使用multiprocessing模块
想多进程间通信可以使用multiprocessin.Manage或queue.Queue( )等来实现