目录

Python多进程学习笔记

前言:

  • 我们前面编写的所有的Python程序,都是执行单任务的进程,也就是只有一个线程。如果我们要同时执行多个任务怎么办?
  • 谈到多任务,就会接触到进程和线程这两个东东;
  • 线程是最小的执行单元,而进程由至少一个线程组成。
  • 目前实现多任务的方式有以下几种:

多进程模式; 多线程模式;(一个进程中开启多个线程) 多进程+多线程模式。

编码实现:

  • 虽然Python 的os模块封装了常见的系统调用,我们可以在linux和mac系统上通过fork操作实现进程的创建,但是windows没有fork调用。
  • 当然,Python是跨平台的,其自然也会提供相应的跨平台的解决方案。
  • multiprocessing模块是一个跨平台版本的多进程模块。该模块提供了process类来代表一个进程对象。

Process

  • 构造方法__init__(self, group=None, target=None, name=None, args=(), kwargs={})

  • 参数说明:

  • group:进程所属组。基本不用

  • target:表示调用对象或方法名称。

  • args:表示调用对象的位置参数元组。

  • name:别名

  • kwargs:表示调用对象的字典。

  • 示例代码如下:

      import os;
      from multiprocessing import Process;
      from time import ctime,sleep
    
      # 该放在运行在自线程中
      def run_proc(process_name):
      	print('child process name is %s,process ID is %s' % (process_name, os.getpid()));
      	print('child process  work end');
    
      print('Parent process ID is %s'% os.getpid());
      print('Parent process start working');
      #target:指定进程执行的函数,args:该函数的参数,需要使用tuple
      p = Process(target=run_proc,args=('child_thread1',));
      print('Child process start working');
      p.start();
      print('Parent process  work end');
    
  • 运行结果如下:

      Parent process ID is 6355
      Parent process start working
      Child process start working
      Parent process  work end
      child process name is child_thread1,process ID is 6356
      child process  work end
    
  • 结果分析:你会发现,在当前进程创建了一个新进程来执行run_proc方法,两个进程的ID是不相同的,而且,在我们创建的进程还没有执行完run_proc方法,当前进程已经运行完成来。

  • 假如我希望run_proc执行结束了,当前进程才继续执行,我们该如何呢?

  • 其实很简单,我们只需要调用 p.join(); 讲创建的进程加入当前进程即可,作用是,知道调用join的这个方法的进程执行完,当前进程才继续执行。

  • join通畅用于进程间同步。

  • 我们修改以下代码如下:

      import os;
      from multiprocessing import Process;
      from time import ctime,sleep
    
      # 该放在运行在自线程中
      def run_proc(process_name):
      	print('child process name is %s,process ID is %s' % (process_name, os.getpid()));
      	print('child process  work end');
    
      print('Parent process ID is %s'% os.getpid());
      print('Parent process start working');
    
      p = Process(target=run_proc,args=('child_thread1',));
      print('Child process start working');
      p.start();
      p.join();
      print('Parent process  work end');
    
  • 运行结果如下:

      Parent process ID is 6433
      Parent process start working
      Child process start working
      child process name is child_thread1,process ID is 6434
      child process  work end
      Parent process  work end
    

线程池 Pool

  • 加入你要创建并启动大量大子进程,此时我们就可以用进程池来管理我们的进程了。
  • Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。

Pool类介绍

  • 1、apply_async():与apply用法一样,但它是非阻塞且支持结果返回进行回调
  • 2、map(): Pool类中的map方法,与Python内置的map函数用法行为基本一致,它会使进程阻塞直到返回结果。 注意,虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。
  • 3、close():关闭进程池(pool),使其不在接受新的任务。
  • 4、terminate():结束工作进程,不在处理未处理的任务。
  • 5、join():主进程阻塞等待子进程的退出,join方法必须在close或terminate之后使用。

示例代码

	import os;
	from multiprocessing import Process,Pool;
	import time,os,random

	# 该放在运行在自线程中
	def run_proc(process_name):
		print('child process name is %s,process ID is %s' % (process_name, os.getpid()));
		start = time.time();
		time.sleep(random.random() * 5);
		end = time.time();
		print('task is %s runs %0.2f seconds.' % (process_name, (end - start)));
		print('child process  work end');

	print('Parent process ID is %s'% os.getpid());
	print('Parent process start working');
	p = Pool(4);
	for i in range(6):
		p.apply_async(run_proc,args=(i,))
		pass
	print('Child process start working');
	p.close();
	p.join();
	print('Parent process  work end');
  • 运行结果如下:

      Parent process ID is 7330
      Parent process start working
      Child process start working
      child process name is 0,process ID is 7331
      child process name is 1,process ID is 7332
      child process name is 2,process ID is 7333
      child process name is 3,process ID is 7334
      task is 0 runs 0.98 seconds.
      child process  work end
      child process name is 4,process ID is 7331
      task is 1 runs 2.10 seconds.
      child process  work end
      child process name is 5,process ID is 7332
      task is 3 runs 2.61 seconds.
      child process  work end
      task is 4 runs 1.91 seconds.
      child process  work end
      task is 5 runs 1.94 seconds.
      child process  work end
      task is 2 runs 4.54 seconds.
      child process  work end
      Parent process  work end
    

结果解析:

  • 如果创建Pool是没传入具体大小,Pool默认创建的数量大小是CPU的核数。
  • 可以看到,因为我们创建Pool是传入4,所以有四个task被四个进程执行了,但是当序号为0的task被执行完后,序号为4的task使用的进程id就是序号为0的task使用的进程,这样就实现了进程的合理利用。