python进阶之系统编程的创建进程和进程进程间通信

python语言中创建进程的方式有fork()、Process(),和进程池三种方法。fork由于不是跨平台的所以在应用中很少,主要以后后两种为主,用得最多的进程池,进程池错做起来相对容易。

一,创建进程:

1、用fork()来创建进程,主要是利用的调用fork()方法后会返回不同的值,一个是0,另一个是大于0的整数,利用这一特点可以进行父子进程的编写,返回值为0代表子进程,大于0的为父进程,父子进程互不干扰,即当父进程结束后子进程仍然可以执行,不会因为父进程的结束而终止子进程的运行,下面的代码例程:

import os
import time

ret = os.fork()
if ret == 0:
	while True:
		print('-------子进程----------')
		time.sleep(1)
else:
	
	while True:
		print('-------父进程----------')
		time.sleep(1)
至于父子进程的返回值问题,看下面的这个例程:
import os
import time

ret = os.fork()
print(ret)
if ret>0:
	print('------父进程-----%s'%os.getpid())	
else:
	print('------子进程-----%s--%s'%(os.getpid(),os.getppid()))
运行结果是:
6587
------父进程-----6586
0
------子进程-----6587--6586
2、Process()创建进程:
如果你打算编写多进程的服务程序,Unix/Linux⽆疑是正确的选择。由于Windows没有fork调⽤,难道在Windows上⽆法⽤Python编写多进程的程序?由于Python是跨平台的,⾃然也应该提供⼀个跨平台的多进程⽀持。multiprocessing模块就是跨平台版本的多进程模块。multiprocessing模块提供了⼀个Process类来代表⼀个进程对象,下⾯的例⼦

演示了启动⼀个⼦进程并等待其结束

from multiprocessing import Process
import time

def test():
	i=100
	while True:
		print('子进程')
		time.sleep(1)
		print('子进程:%d'%i)
p = Process(target =test)
p.start()#同时运行父子进程,也就是开启子进程
i=0#用于判断是否存在进程间的变量共享
#p.run()	#用子进程覆盖父进程
while True:
	print('父进程')
	time.sleep(1)
	i += 1
	print(i)
	if i==5:
		p.terminate()	#杀死子进程
注意:创建⼦进程时,只需要传⼊⼀个执⾏函数和函数的参数,创建⼀个Process实例,⽤start()⽅法启动,这样创建进程⽐fork()还要简单。join()⽅法可以等待⼦进程结束后再继续往下运⾏,通常⽤于进程间的,换句话说就是,如果不调用join()函数,在主进程结束后,程序就会终止,不管子程序是否执行玩,这一点和fork()不同

Process()的子类创建进程:创建新的进程还能够使⽤类的⽅式,可以⾃定义⼀个类,继承Process类,每次实例化这个类的时候,就等同于实例化⼀个进程对象,请看下⾯的实例:

from multiprocessing import Process
import time

class sonProcess(Process):
	def run(self):#重写run方法,将父类中的run方法覆盖,其他方法不变,在父类的run方法中实质上是定义子进程运行的节点
			#即当调用start()方法时,start方法内会自动调用run方法,并将获取的子进程的运行节点给run 方法,内部在调用run 
			#因为如果只调用run 方法的话,程序会只运行子进程
		while True:
			print('子进程的run()')
			time.sleep(1)

p1 = sonProcess()
p1.start()#利用继承的思想,p会调用从父类中继承来的start()方法

while True:
	print('父进程')
	time.sleep(1)

3利用进程池进行子进程的创建:当需要创建的⼦进程数量不多时,可以直接利⽤multiprocessing中的Process动态成⽣多个进程,但如果是上百甚⾄上千个⽬标,⼿动的去创建进程的⼯作量巨⼤,此时就可以⽤到multiprocessing模块提供的Pool⽅法。初始化Pool时,可以指定⼀个最⼤进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建⼀个新的进程⽤来执⾏该请求;但如果池中的进程数已经达到指定的最⼤值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执⾏,请看下⾯的实例:
from multiprocessing import Pool
import time
import os


def worker(num):
	for i in range(5):
		print('----第%d次--pid=%s------num=%d--'%(i,os.getpid(),num))
		time.sleep(1)

pool = Pool(4)

for i in range(10):
	print('=========%d========='%i)
	pool.apply_async(worker,(i,))#非堵塞式添加任务(常用方式)
#	pool.apply(worker,(i,))#堵塞式添加任务(极少使用),感觉又又变回了单进程运行程序,只是不用手动启动其他子进程而已,实现了自动的效果


pool.close()#关闭进程池,表示不再向进程池中添加进程
pool.join()#主进程 创建/添加 进程结束后,主进程默认不会等待子进程结束在终止整个进程的运行,(即不像Process创建进程一样,而是和fork创建进程相似		#但是又不尽相同,fork创建的子进程,在父进程结束以后子进程仍然可以单独执行,只是主进程结束了,而Pool进程池在主进程结束后,当即		#终止进程池中所有进程)
		
二、进程间的通信(进程间通信的方式有多种,下面只描述利用队列进行进程间通信):

1、Process():Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。可以使⽤multiprocessing模块的Queue实现多进程之间的数据传递,Queue
本身是⼀个消息列队程序,

说明:初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最⼤可接收
的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到
内存的尽头);
Queue.qsize():返回当前队列包含的消息数量;
Queue.empty():如果队列为空,返回True,反之False ;
Queue.full():如果队列满了,返回True,反之False;
Queue.get([block[, timeout]]):获取队列中的⼀条消息,然后将其从列队
中移除,block默认值为True;
1)如果block使⽤默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为⽌,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;

2)如果block值为False,消息列队如果为空,则会⽴刻抛出"Queue.Empty"异常;Queue.get_nowait():相当Queue.get(False);Queue.put(item,[block[, timeout]]):将item消息写⼊队列,block默认值为True;
1)如果block使⽤默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写⼊,此时程序将被阻塞(停在写⼊状态),直到从消息列队腾出空间为⽌,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
2)如果block值为False,消息列队如果没有空间可写⼊,则会⽴刻抛出"Queue.Full"异常;Queue.put_nowait(item):相当Queue.put(item, False);

下面是一个利用两个进程,实现读写功能的程序:

#利用队列进行进程间通信,注意队列的数据存储特点,先进先出,(区别栈的特点,先进后出)
from multiprocessing import Process,Queue
import os,time

def write(q):
	for value in ['a','b','c','d']:
		if not q.full():
			q.put(value)
			print('========put>>value:%s=========='%value)
			time.sleep(1)
		else:
			print('写入暂停!')
			
def read(q):
	while True:
		if not q.empty():
			print('========get>>value:%s======='%q.get())
			time.sleep(1)
		else:
			print('读取结束!')
			break


if __name__=='__main__':
	q = Queue(3)
#	q = Queue()
	pw = Process(target=write,args=(q,))	#利用Process()分别创建两个子进程,并返回该进程的pid
	pr = Process(target=read,args=(q,))
	
	pw.start()#通过进程的pid调用start()方法,开启进程
	pr.start()

	pr.join()#阻塞进程,避免该进程结束退出程序
	pw.join()
2、进程池中的Queue如果要使⽤Pool创建进程,就需要使⽤multiprocessing.Manager()中的Queue(),⽽不是multiprocessing.Queue(),否则会得到⼀条如下的错误信息:python基础语⾔进程间通信-Queue 31RuntimeError: Queue objects should only be shared between processesthrough inheritance.下⾯的实例利用进程池实现上面的程序:
#利用队列进行进程池中进程间通信,注意队列的数据存储特点,先进先出,(区别栈的特点,先进后出)
from multiprocessing import Pool,Manager
import os,time

def write(q):
	for value in ['a','b','c','d']:
		if not q.full():
			q.put(value)
			print('========put>>value:%s=========='%value)
			time.sleep(1)
		else:
			print('写入暂停!')
			
def read(q):
	while True:
		if not q.empty():
			print('========get>>value:%s======='%q.get())
			time.sleep(1)
		else:
			print('读取结束!')
			break


if __name__=='__main__':
	q = Manager().Queue(3)# 限制队列元素个数
#	q =Manager().Queue()	
	pool = Pool(3)
#	pool.apply_async(write,(q,))  #非堵塞式添加进程
	pool.apply(write,(q,))	#堵塞式添加进程
#	pool.apply_async(read,(q,))
	pool.apply(read,(q,))
	
	pool.close()	#关闭进程池,停止添加进程

	pool.join()	#防止主进程结束后随即杀死子进程

#进程池创建多进程运行的步骤:
#创建进程池对象 	pool = Pool() 可以添加限制进程个数
#调用apply()或者是zpply_async()添加进程
#停止添加进程 pool.close()
#防止主进程杀死子进程 pool.join()


补充:进程间通信的方式:

# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
# 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
# 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
# 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值