多进程
1、GIL 导致线程不能利用多核优势
2、多进程实现方式(2)
multiprocessing 开启子进程
Process 创建进程
1)函数式
import multiprocessing,time
def run(name):
time.sleep(5)
print("来了,老弟儿",name)
if __name__ == '__main__':
p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"},name="l1")
p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"},name="l2")
p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"},name="l3")
p1.start()
p2.start()
p3.start()
print(p1.name)
2)继承式
import multiprocessing,time
class MyProcess(multiprocessing.Process):
def __init__(self):
multiprocessing.Process.__init__(self)
def run(self):
print("来了,老弟儿")
if __name__=="__main__":
p1 = MyProcess()
p2 = MyProcess()
p1.start()
p2.start()
3、terminate is_alive join daemon name pid
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置。
p.name:进程的名称
p.pid:进程的pid
4、进程同步(锁)(Lock互斥锁)
import multiprocessing,time
def work(name,lock):
lock.acquire()
with open("b.txt",mode="r",encoding="utf-8") as f:
c = int(f.read())
print(c)
c -= 1
time.sleep(1)
print(name,c)
with open("b.txt", mode="w", encoding="utf-8") as f1:
f1.write(str(c))
lock.release()
if __name__ == '__main__':
lock = multiprocessing.Lock()
p1 = multiprocessing.Process(target=work,args=("p1",lock))
p2 = multiprocessing.Process(target=work,args=("p2",lock))
p3 = multiprocessing.Process(target=work,args=("p3",lock))
p1.start()
p2.start()
p3.start()
5、进程间通信:队列 管道
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
maxsize:队列中允许最大项数,省略则无大小限制。
主要方法:
q.put方法用以插入数据到队列中put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间, 直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
import multiprocessing,time
def work(q):
if not q.empty():
print(q.get())
if __name__ == '__main__':
q = multiprocessing.Queue(maxsize=3)
q.put(1)
q.put(2)
q.put(3)
p1 = multiprocessing.Process(target=work,args=(q,))
p2 = multiprocessing.Process(target=work,args=(q,))
p1.start()
p2.start()
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。
主要方法:
conn1.recv():接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象
conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
conn1.fileno():返回连接使用的整数文件描述符
conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。
conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收
conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。
import multiprocessing
def jieshou(left,right):
right.close()#关闭连接
print(left.recv())#接收
def fasong(left,right):
left.close()
right.send("我是被发送的数据")#发送
if __name__ == '__main__':
left,right = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=jieshou,args=(left,right))
p2 = multiprocessing.Process(target=fasong,args=(left,right))
p1.start()
p2.start()
6、进程池
Pool([numprocess [,initializer [, initargs]]]):创建进程池
numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
initializer:是每个工作进程启动时要执行的可调用对象,默认为None
initargs:是要传给initializer的参数组
方法介绍:
p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()
p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。
p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成5 P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。
obj.ready():如果调用完成,返回True
obj.successful():如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
obj.wait([timeout]):等待结果变为可用。
obj.terminate():立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数