python--多进程的用法详解实例


想让python实现多进程(multiprocessing),我们要先区分不同的操作系统的不同之处。

Linux操作系统下提供了一个fork()系统调用,普通函数调用一次返回一次,fork()调用一次返回两次,因为操作系统自动把当前进程(父进程)复制了一份(称为子进程),然后分别在父进程和子进程内返回。

子进程永远返回0,而父进程则是返回子进程的ID,因为父进程可以fork出很多的子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

python的os模块封装了常见的系统调用,其中就包括fork(),可以在python程序中很容易的创建出子进程:

import os 

#注意此代码只适用于like Linux环境下

print('进程{}开始了...').format(os.getpid())
pid = os.fork()
if pid == 0:
  print("我是子进程:{},我的父进程是:{}").format(os.getpid(), os.getppid())
else:
    print("本进程:{},只创造了一个子进程:{}").format(os.getpid(), pid)

运行结果如下:

进程567开始了...
我是子进程568,我的父进程是567
本进程567,只创造了一个子进程568

由于Windows没有fork调用,上面的代码无法再windows上运行,由于Mac系统是基于BSD内核(Unix的一种),所以可以在Mac上直接运行。
有了fork调用,一个进程在接到一个新任务的时候就可以复制出一个子进程来处理新任务,常见的Apache服务器就是有父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。


multiprocessing

如果你想编写多进程的服务程序,Unix/Linux毫无疑问是较好的选择。同时由于Windows上没有fork调用,难道我们就没有办法在Windows上用python写多进程的程序?当然不是!!

因为python是跨平台的,自然也会提供跨平台的多进程支持,multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个多进程的对象,下面上实例代码:

from multiprocessing import Process
import os


'''
这是多进程的代码演示
ps:本人的演示代码环境为python2.7(此版本下可加可不加,python很强的可以前后兼容)
python3.x版本下依然可以运行,只需要print的内容加上括号(规范啊规范少年们)
'''

#子进程要执行的代码
def run_proc(name):
    print ('run child process {},{}').format(name,os.getpid())

if __name__ == '__main__':
    print 'parent process {}'.format(os.getpid())
    p = Process(target=run_proc,args=('test',))
    print 'child process will start'
    p.start()
    p.join()
    print 'child process end'

运行结果如下:

parent process 8556
child process will start
run child process test,2176
child process end

在创建子进程的时候,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。


Pool(进程池)

如果我们要启动大量的子进程,可以用进程池的方式批量创建子进程:

from multiprocessing import Pool
import os,time,random


'''
这是多进程的代码演示
ps:本人的演示代码环境为python2.7(此版本下可加可不加,python很强的可以前后兼容)
python3.x版本下依然可以运行,只需要print的内容加上括号(规范啊规范少年们)
'''

#子进程要执行的代码
def proc_task(name):
    print ('运行子进程 {}号,({})').format(name,os.getpid())
    start_time = time.time()
    time.sleep(random.random()*3)#随机延时一波
    end_time = time.time()
    print ('子进程 %s 号 运行了 %0.2f 秒.' % (name, (end_time - start_time)))#此处为百分号占位符,和format作用一样

if __name__ == '__main__':
    print ("父进程是{}").format(os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(proc_task,args=(i,))
    print ("等待所有子进程执行完毕...")
    p.close()
    p.join()
    print ("所有子进程执行完成")

运行结果为:

父进程是8796
等待所有子进程执行完毕...
运行子进程 0号,(8052)
运行子进程 1号,(12648)
运行子进程 2号,(14200)
运行子进程 3号,(8016)
子进程 2 号 运行了 0.40 秒.
运行子进程 4号,(14200)
子进程 3 号 运行了 1.57 秒.
子进程 1 号 运行了 2.08 秒.
子进程 0 号 运行了 2.90 秒.
子进程 4 号 运行了 2.52 秒.
所有子进程执行完成
光写不讲是坏蛋,下面进入代码解读:

对Pool对象调用join()方法会等待所有子进程执行完毕,调用join之前呢必须先调用close(),调用close()之后就不能继续添加新的Process了。(ps:什么?你问我为什么?门都关上(close)了,还进人(添加新的process),那不得撞得流鼻血(报错)?)


子进程

很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还要控制子进程的输入和输出。
subprocess模块可以让我们非常方便的启动一个子进程,然后控制其输入和输出。
下面上一个实例(演示了如何在Python代码中运行命令ipconfig,这和命令行直接运行的效果是一样的):

import subprocess

'''
这是多进程的代码演示
ps:本人的演示代码环境为python2.7(此版本下可加可不加,python很强的可以前后兼容)
python3.x版本下依然可以运行,只需要print的内容加上括号(规范啊规范少年们)
'''
print ("$ ipconfig")
r = subprocess.call(['ipconfig'])
print ('exit code',r)

运行结果如下:

Windows IP 配置


无线局域网适配器 WLAN:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . :

无线局域网适配器 本地连接* 1:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . :

以太网适配器 以太网:

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::80b3:647f:c533:86c1%19
   IPv4 地址 . . . . . . . . . . . . : 192.168.1.132
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : 192.168.1.1
#.........(后面太长就省略掉,知道意思就行,不水)

进程间的通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。python的multiprocessing模块包装了底层的机制,提供了Queue,Pipe等多种方式来交换数据。

在这里我们以较为常用的Queue为例子,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读取数据:

from multiprocessing import Process,Queue
import os,time,random

'''
这是多进程的代码演示
ps:本人的演示代码环境为python2.7(此版本下可加可不加,python很强的可以前后兼容)
python3.x版本下依然可以运行,只需要print的内容加上括号(规范啊规范少年们)
'''

#写数据进程
def write_data(q):
    print ('我是写入进程:{}').format(os.getpid())
    for v in ['a','b','c']:
        print ('把{}写入队列...').format(v)
        q.put(v)
        time.sleep(random.random())

#读数据进程
def read_data(q):
    print ('我是读取进程:{}').format(os.getpid())
    while True:
        value = q.get(True)
        print ('从队列里读取到{}...').format(value)

if __name__ == '__main__':
    #父进程创建queue,并传递给各个子进程:
    q = Queue()
    pw = Process(target=write_data,args=(q,))
    pr = Process(target=read_data, args=(q,))
    #启动子进程pw,写入数据
    pw.start()
    #启动子进程pr,读取数据
    pr.start()
    #等待pw结束
    pw.join()
    #pr,进程里是死循环,无法等待其自动结束,所以手动结束
    pr.terminate()

运行结果如下:

我是读取进程:14700
我是写入进程:9772
把a写入队列...
从队列里读取到a...
把b写入队列...
从队列里读取到b...
把c写入队列...
从队列里读取到c...

在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了。

小结一波

在like Linux环境下,可以使用fork()调用实现多进程。

要实现跨平台的多进程,可以使用multiprocessing模块。

进程间通信是通过Queue、Pipes等实现的。


多进程的讲解就到这里,后面会给大家继续分享关于python多线程的知识,本人技术一般,不喜勿喷,欢迎评论或者私信指教,有空我会一一回复,共同学习共同进步!
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 1.进程 1.1多线程的引入 现实生活中 有很多的场景中的事情是同时进行的,比如开车的时候手和脚共同来驾驶汽车,再...
    TENG书阅读 495评论 0 0
  • 一、进程的概念 相信很多同学都听说过windows、linux,MacOS都是多任务,多用户的操作系统。那什么是多...
    转身后的那一回眸阅读 962评论 0 1
  • 现在, 多核CPU已经非常普及了, 但是, 即使过去的单核CPU, 也可以执行多任务。 CPU执行代码都是顺序执行...
    LittlePy阅读 4,786评论 0 3
  • 初见难相忘, 纯然冠群芳。 似雪稀春色, 却添两岸香。
    枫之然阅读 343评论 22 24
  • 你看世界 我看你
    并向你扔了一条狗敷衍彡阅读 294评论 1 1