进程–multiprocessing模块
一、知识梳理
1,进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
2,线程
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
3,并发与并行
无论是并行还是并发,在用户看来都是’同时’运行的,不管是进程还是线程,都只是一个任务而已,真正在干活的是cpu,cpu来做这些任务,而一个cpu(单核)同一时刻只能执行一个任务
并发:看起来是同时运行。单个cpu+多道技术就可以实现并发,(并行也属于并发)
并行:任务同时在运行,只有cpu多核时才行
python的Gil特性下,根据任务种类的不同(io密集型、计算(cpu)密集型),选择使用多进程还是多线程。
4,同步与异步
同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。按照这个定义,其实绝大多数函数都是同步调用。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务
异步的概念和同步相对。当一个异步功能调用发出后,调用者不能立刻得到结果。当该异步功能完成后,通过状态、通知或回调来通知调用者。如果异步功能用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低。如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。
二、Process类的介绍
python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 进程名;
args/kwargs: 要传入方法的参数。
实例方法:
is_alive():返回进程是否在运行。
join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
start():进程准备就绪,等待CPU调度
run():strat()调用run方法,如果实例进程时未指定传入target,这start执行默认run()方法。
terminate():不管任务是否完成,立即停止工作进程
属性:
daemon:和线程的setDeamon功能一样
name:进程名字。
pid:进程号。
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
三、Process使用
#直接使用Process类生成子进程
from multiprocessing import Process
import os,time
def test(num):
print(num)
print(time.ctime())
print(os.getpid())
if __name__ == '__main__':
# p1.start()
p = []
for i in range(10):
p1 = Process(target=test,args=(i,))
p.append(p1)
print(p)
for i in p:
i.start()
for i in p:
i.join()
print (time.ctime ())
print (os.getpid ())
print('the end')
[<Process(Process-11, initial)>, <Process(Process-12, initial)>, <Process(Process-13, initial)>, <Process(Process-14, initial)>, <Process(Process-15, initial)>, <Process(Process-16, initial)>, <Process(Process-17, initial)>, <Process(Process-18, initial)>, <Process(Process-19, initial)>, <Process(Process-20, initial)>]
Thu May 24 21:24:41 2018
4440
the end
#自定义类继承Process类
from multiprocessing import Process
import os,time
class Myprocess(Process):
def __init__(self,num):
super().__init__()
self.num = num
def run(self):
print(self.num)
print(time.ctime())
print(os.getpid())
if __name__ == '__main__':
p = []
for i in range(10):
p1 = Myprocess(i)
p.append(p1)
print(p)
for i in p:
i.start()
for i in p:
i.join()
print (time.ctime ())
print (os.getpid ())
print('the end')
[<Myprocess(Myprocess-1, initial)>, <Myprocess(Myprocess-2, initial)>, <Myprocess(Myprocess-3, initial)>, <Myprocess(Myprocess-4, initial)>, <Myprocess(Myprocess-5, initial)>, <Myprocess(Myprocess-6, initial)>, <Myprocess(Myprocess-7, initial)>, <Myprocess(Myprocess-8, initial)>, <Myprocess(Myprocess-9, initial)>, <Myprocess(Myprocess-10, initial)>]
Thu May 24 21:24:18 2018
4440
the end
四、进程间通信
1,进程队列–Queue
from multiprocessing import Process,Queue
import os,time
q = Queue()
def putit(q):
q.put('123')
q.put('abc')
q.put({'ab':12})
print(os.getpid())
def getit():
print(q.get())
print(q.get())
print(q.get())
print (os.getpid ())
if __name__ == '__main__':
p = Process(target=putit,args=(q,))
print(p.name,p.pid)
p.start()
p.join()
getit()
2,管道–Pipe
from multiprocessing import Process, Pipe
def f(conn):
conn.send('hello master')
res = conn.recv()
print('回复信息:',res)
conn.close()
print('q2_id:',id(conn))
if __name__ == "__main__":
parent_conn,child_conn = Pipe()
print('q1_id:',id(child_conn))
p = Process(target=f,args=(child_conn,))
p.start()
print(parent_conn.recv())
parent_conn.send('hello saber')
3,Manager
Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。
from multiprocessing import Process, Manager
def f(d, l,n):
d[n] = '1' #{0:"1"}
d['2'] = 2 #{0:"1","2":2}
l.append(n) #[0,1,2,3,4, 0,1,2,3,4,5,6,7,8,9]
#print(l)
if __name__ == '__main__':
with Manager() as manager: #manager = Manager()
d = manager.dict()#{}
l = manager.list(range(5))#[0,1,2,3,4]
p_list = []
for i in range(10):
p = Process(target=f, args=(d,l,i))
p.start()
p_list.append(p)
for res in p_list:
res.join()
print(d)
print(l)
{}
[0, 1, 2, 3, 4]
五、进程同步—同步锁
为了解决进程间互相抢占某一资源,比如输出,使用同步锁
from multiprocessing import Process,Lock
import time
def f(l,i):
with l:
#l.aqquire() 如果不用with
print('hello %s'%i)
time.sleep(1)
#l.release()
if __name__ == '__main__':
lock = Lock()
for i in range(10):
Process(target=f,args=(lock,i)).start()
六、进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
apply
apply_async
多进程是实现并发的手段之一,需要注意的问题是:
1、很明显需要并发执行的任务通常要远大于核数
2、一个操作系统不可能无限开启进程,通常有几个核就开几个进程
3、进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)
例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
from multiprocessing import Process,Pool
import time,os
def Foo(i):
time.sleep(1)
print(i)
print("son",os.getpid())
return "HELLO %s"%i
def Bar(arg):
print(arg)
# print("hello")
# print("Bar:",os.getpid())
if __name__ == '__main__':
pool = Pool(5)
print("main pid",os.getpid())
for i in range(100):
#pool.apply(func=Foo, args=(i,)) #同步接口
#pool.apply_async(func=Foo, args=(i,))
#回调函数: 就是某个动作或者函数执行成功后再去执行的函数
pool.apply_async(func=Foo, args=(i,),callback=Bar)
pool.close()
pool.join() # join与close调用顺序是固定的
print('end')
七、协程
协程,又称微线程,纤程。英文名Coroutine。
优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
#yield实现
import time
def consumer(name):
print('-----ready to eat-----')
while 1:
new_sushi = yield
print('[%s] is eating sushi %s'%(name,new_sushi))
time.sleep(1)
def producer():
r = cook.__next__()
r = cook2.__next__ ()
n = 0
while 1:
time.sleep(1)
print('[producer] is making sushi %s and %s'%(n,n+1))
cook.send(n)
cook.send(n+1)
n +=2
if __name__ == '__main__':
cook = consumer('consumer1')
cook2 = consumer('consumer2')
p = producer()
greedlet模块
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
def test2():
print(56)
gr1.switch()
print(78)
gr1.switch()
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr2.switch()
Gevent模块
import gevent
import requests,time
start=time.time()
def f(url):
print('GET: %s' % url)
resp =requests.get(url)
data = resp.text
print('%d bytes received from %s.' % (len(data), url))
gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://www.baidu.com/'),
gevent.spawn(f, 'https://www.sina.com.cn/'),
])
print("cost time:",time.time()-start)