前言
前面我们了解了计算机中的多任务原理,知道了进程和线程的概念。今天就通过代码看Python中多进程是如何实现的。
Linux\Unix多进程:os.fork()
操作系统提供了一个系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
返回结果:子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用就可以拿到父进程的ID。
Python的模块封装了常见的系统调用,其中就包括,可以在Python程序中轻松创建子进程:
提示:只在系统下可以使用。Mac系统是基于Unix系统开发的,所以也支持这种写法。如果你是在系统下做的Python开发,那么运行上面代码,会报错:
由于没有调用,但Python是支持跨平台的,所以上用Python编写多进程的程序,就需要借用模块了。
跨平台多进程:multiprocessing
模块提供了一个类来代表一个进程对象。通常可导入模块:
示例:
运行结果:
提示:上面例子中方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。说白了就是,父进程的结束时间是要等子进程结束后才结束。如果不调用此方法,运行结果如下:
父进程的结束时间不再受子进程结束的影响。父进程可能在子进程结束之前就结束,也可能在子进程结束后才结束,具体情况要看各自进程的具体操作。本例中,如果不调用方法,但子进程有耗时操作,而父进程中没有耗时的操作,所以父进程优先于子进程结束了。
说明:
1,创建子进程时,只需要传入一个执行函数和函数的参数,创建一个实例,用方法启动,这样创建进程比还要简单。
2.创建子进程时,切不可这样创建:。为了方便对比,将两种写法拿出来:
参数的传入方式不同,上面例子中通过传入,形如:,而错误的写法是直接通过指定时就一起传入,形如:。
错误的创建形式虽然可以创建子进程成功,但会导致:方法无论是否调用,都会出现进程同步的现象。即:父进程的结束时间是要等子进程结束后才结束。
线程池Pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程。模块提供了一个类来代表一个进程对象。通常可导入模块:
类可以提供指定数量的进程供用户调用,当有新的请求提交到中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。
下面介绍一下模块下的类下的几个方法:
函数原型:
该函数用于传递不定参数,同python中的apply函数一致,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不在出现)。
函数原型:与用法一致,但它是非阻塞的且支持结果返回后进行回调。
函数原型:
类中的方法,与内置的函数用法行为基本一致,它会使进程阻塞直到结果返回。
注意:虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。
函数原型:
与map用法一致,但是它是非阻塞的。其有关事项见。
关闭进程池pool,使其不在接受新的任务。
结束工作进程,不在处理未处理的任务。
主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用。
下面我们看一个简单的类的例子:
运行结果:
对Pool对象调用方法会等待所有子进程执行完毕,调用之前必须先调用,调用之后就不能继续添加新的了。
进程间通信
之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的模块包装了底层的机制,提供了等多种方式来交换数据。
我们以为例,在父进程中创建两个子进程,一个往里写数据,一个从里读数据:
运行结果:
小结
本文主要学习了python中的多进程及实现跨平台的多进程,进程池、通过进行进程间通信。
人人懂编程ID:pythonDNA
领取专属 10元无门槛券
私享最新 技术干货