python进程与、消息队列Queue、进程池Pool的结合

进程

进程:通俗理解一个运行的程序或者软件,进程是操作系统资源分配的基本的单位。

  • 现实生活中的公司可以理解成是一个进程,公司提供办公资源,员工可以理解为线程。

进程的状态

工作中,任务数往往大于CPU的核数,即一定有一些任务正在执行,而另外一些任务在等待CPU进行执行,因此导致了有了不同的状态。

  • 一个进程默认有一个线程,进程里面可以创建线程,线程是依附在进程里面的,没有进程就没有线程。

多进程完成多任务

导入进程模块

import multiprocessing

Process进程类

  • group : 指定进程组,目前只能使用None
  • target : 执行的目标任务名
  • name : 进程名字
  • args : 以元组方式执行任务传参
  • kwargs : 以字典方式给执行任务传参

Process创建的实例对象的常用方法

  • start(): 启动子进程实例(创建子进程)
  • join([timeout]):是否等待子进程执行结束,或等待多少秒
  • terminate:不管任务是否完成,立即终止子进程。

Process创建的实例对象的常用属性:

  • name: 当前进程的别名,默认为Process-N,N为从1开始递增的整数。
  • pid:当前进程的pid(进程号)

多进程完成多任务代码

import multiprocessing
import time

def run_proc():
	while True:
		print("----2----")
		time.sleep(1)

if __name__=='__main__':
	sub_process = multiprocessing.Process(target=run_proc)
	sub_process.start()
	while True:
		print("----1----")
		time.sleep(1)

结果:
----1----
----2----
----1----
----2----
----1----
----2----

获取进程pid

import multiprocessing
import time
import os

def work():
	current_process = multiprocessing.current_process()
	print("work:", current_process) # 查看当前进程
	print("work的进程编号为:", current_process.pid, os.getpid()) # 获取当前进程的编号
	print("work的父进程编号为:", os.getpid())

	for i in range(10):
		print("工作中....")
		time.sleep(0.2)
		os.kill(os.getpid(), 9)
	
if __name__=='__main__':
	current_process = multiprocessing.current_process()
	print("main:", current_process) # 获取当前进程

	print("main进程的编号:", current.pid) # 获取当前进程的编号

	sub_process = multiprocessing.Process(target=work)
	
	sub_process.start()

	for i in range(20):
		print("我在主进程汇总执行...")
		time.sleep(0.2)
结果:
main: <_MainProcess(MainProcess, started)>
main进程的编号: 67264
我在主进程中执行...
work: 
work进程编号: 71956 71956
work父进程的编号: 67264
工作中....
我在主进程中执行...
我在主进程中执行...

给子进程指定的函数传递参数

import multiprocessing

def show_info(name, age):
	print(name, age)


if __name__=='__main__':
	sub_process= multiprocessing.Process(target=show_info, name="myproess", args=("xiaof", 18))

sub_process.start()
结果:
xiaof 18

进程注意点:

  • 进程之间不共享全局变量

  • 创建子进程其实是对主进程进行拷贝,进程之间相互独立,访问的全局变量不是同一个,所以进程之间不共享全局变量。

  • 主进程会等待所有的子进程执行完成程序再推出

    work_process.terminate() # 销毁子进程

进程间通信-Queue

Queue的使用

可以使用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;

3.如果block使用默认值,且没有设置timeout(单位秒),消息队列如果已经没有空间可写入,此时程序将被阻塞(停在写入状态), 直到从消息队列腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出“Queue.Full”异常;

4.如果block值为False,消息队列如果没有空间可写入,则会立刻抛出“Queue.Full”异常;

  • Queue.put_nowait(item) : 相当Queue.put(item,False);

消息队列Queue完成进程通信的演练

以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读取数据;

import multiprocessing
import time

def write_data(queue):
	for i in range(10):
		if queue.full():
			print("队列满了")
			break
		queue.put(i) # 将item消息写入队列,block默认值为True;
		time.sleep(0.2)
		print(i)


def read_data(queue):
	while True:
		if queue.qsize() == 0:
			print("队列空了")
			break
		value = queue.get() # 获取队列中的一条消息,然后将其从列表中移除,block默认值为True;
		print(value)


if __name__=='__main__':
	queue = multiprocessing.Queue(5) # 创建消息队列

	write_process = multiprocessing.Process(target=write_data,args=(queue,)) # 创建写入数据的进程

	read_process = multiprocessing.Process(target = read_data, args=(queue,)) # 创建读取数据的进程

	write_process.start() # 启动进程

	write_process.join() # 主进程等待吸入进程执行完成以后代码要继续往下执行

	read_process.start()

结果:
0
1
2
3
4
队列满了
0
1
2
3
4
队列空了
  • 从队列取值使用get方法,向队列放入值使用put方法,
  • 消息队列判断队列是否为空不可靠,可以使用延时和根据个数进程判断。

进程池Pool

池子里面放的是进程,进程池会根据任务执行情况自动创建进程,而且尽量少创建进程,合理使用进程池中的进程完成多任务。

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求,但是如果池中的进程数已经达到指定的最大值,那么该请求就会等待,知道池中有进程结束,才会用之前的进程来执行新的任务。

进程池同步执行任务

进程池同步执行任务表示进程池中的进程在执行任务的时候一个执行完成另一个才能执行,如果没有执行完会等待上一个进程执行。

进程池同步执行任务

import multiprocessing
import time

def work():
	print("复制中...", multiprocessing。current_process().pid)
	time.sleep(0.5)

if __name__=='__main__':
	创建进程池
	3:进程池中进程的最大个数
	pool = multiprocessing.Pool(3)
	模拟大批量的任务,让进程池去执行

	for i in range(5):
		循环让进程池执行对应的任务
		同步执行任务,一个任务执行完成后另一个任务才能执行
		pool.apply(work)

结果:
复制中... 100512
复制中... 68128
复制中... 98924
复制中... 100512
复制中... 68128	

进程池异步执行任务

进程池异步执行任务表示进程池中的进程同时执行任务,进程之间不会等待进程池异步实例代码:

import multiprocessing
import time

def work():
	print("复制中...", multiprocessing.current_process().pid) 
	time.sleep(0.5)

if __name__ == '__main__':
创建进程池
3:进程池中进程的最大个数
pool = multiprocessing.Pool(3)
模拟大批量的任务,让进程池去执行
for i in range(5):
    循环让进程池执行对应的work任务
    同步执行任务,一个任务执行完成以后另外一个任务才能执行
    pool.apply(work)
    异步执行,任务执行不会等待,多个任务一起执行
    pool.apply_async(work)

关闭进程池,意思告诉主进程以后不会有新的任务添加进来
pool.close()
主进程等待进程池执行完成以后程序再退出
pool.join()

结果:
复制中... 122872
复制中... 61772
复制中... 114636
复制中... 122872
复制中... 114636

小结:

multiprocessing.Pool常用函数解析:

  • apply(func,args, kwds): 阻塞方式调用函数,kwes表示以子典方式给函数传参。

  • apply_asyncting(func, args, kwds) : 使用非阻塞方式调用函数,args表示以元组方式给函数传参,kwds 表示以字典方式给函数传参

  • close(): 关闭Pool,使其不能接受新的任务

  • terminate(): 不管任务是否完成,立即终止。

  • join(): 主进程阻塞,等待子进程的退出,必须在close或terminate之间使用。

你可能感兴趣的:(Python)