Python并发编程-多进程

                       作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.多进程相关概念

  由于Python的GIL全局解释器锁存在,多线程未必是CPU密集型程序的好的选择。

  多进程可以完全独立的进程环境中运行程序,可以较充分地利用多处理器。

  但是进程本身的隔离带来的数据不共享也是一个问题。而且线程比进程轻量级。

二.multiprocessing

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import multiprocessing
import datetime """
  Process类遵循了Thread类的API,减少了学习难度。   相比线程的方法,进程多出来几个方法需要注意下:
    pid:
      进程id
    exitcode:
      进程的退出状态码
    terminate():
      终止指定的进程 先看一个例子,前面介绍的单线程、多线程比较的例子的多进程版本,具体代码如下所示。
""" def calc(i):
sum = 0
for _ in range(1000000000):
sum += 1
return i,sum if __name__ == '__main__':
start = datetime.datetime.now() ps = [] for i in range(4):
p = multiprocessing.Process(target=calc,args=(i,),name="calc-{}".format(i))
ps.append(p)
p.start() for p in ps:
p.join()
print(p.name,p.exitcode) delta = (datetime.datetime.now() - start).total_seconds()
print(delta) for p in ps:
print(p.name,p.exitcode) print("=== end ===")
calc-
calc-
calc-
calc-
47.011848
calc-
calc-
calc-
calc-
=== end ===

以上代码执行结果戳这里

三.进程间同步

  Python在进程间同步提供了和线程同步一样的类,使用的方法一样,使用的效果也类似。

  不过,进程间代价要高于线程间,而且系统底层实现是不同的,只不过Python屏蔽了这些不同之处,让用户简单使用多进程。
  multiprocessing还提供共享内存、服务器进程来共享数据,还提供了用于进程间通讯的Queue队列,Pipe管道。

  通信方式不同
    .多进程就是启动多个解释器进程,进程间通信必须序列化、反序列化
    .数据的线程安全性问题

  如果每个进程中没有实现多线程,即每个进程仅有一个线程,此时GIL可以说没什么用了。

四.进程池举例

1>.multiprocessing.Pool 是进程池类

multiprocessing.Pool 是进程池类,他有很多方法,具体如下:
  apply(self, func, args=(), kwds={}):
    阻塞执行,导致主进程执行其他子进程就像一个个执行

  apply_async(self, func, args=(), kwds={},callback=None, error_callback=None):
    与apply方法用法一致,非阻塞异步执行,得到结果后会执行回调

  close():
    关闭池,池不能再接受新的任务,所有任务完成后退出进程

  terminate():
    立即结束工作进程,不再处理未处理的任务
  
  join():
    主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用

2>.同步调用

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import logging
import datetime
import multiprocessing
# 日志打印进程id、进程名、线程id、线程名
logging.basicConfig(level=logging.INFO, format="%(process)d %(processName)s %(thread)d %(message)s")
def calc(i):
sum = 0
for _ in range(1000000000): # 10亿
sum += 1
logging.info(sum)
return i,sum # 进程要return,才可以拿到这个结果 if __name__ == '__main__': # 注意一定要有这一句
start = datetime.datetime.now() pool = multiprocessing.Pool(4) for i in range(4):
# 返回值,同步调用,注意观察CPU使用
ret = pool.apply(calc, args=(i,))
print(ret)
pool.close()
pool.join() delta = (datetime.datetime.now() - start).total_seconds()
print(delta)
print('===end====')
calc-0 0
calc-1 0
calc-2 0
calc-3 0
50.399442
calc-0 0
calc-1 0
calc-2 0
calc-3 0
=== end ===

以上代码执行结果戳这里

3>.异步调用

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import logging
import datetime
import multiprocessing
# 日志打印进程id、进程名、线程id、线程名
logging.basicConfig(level=logging.INFO, format="%(process)d %(processName)s %(thread)d %(message)s") def calc(i):
sum = 0
for _ in range(1000000000): # 10亿
sum += 1
logging.info(sum)
return i, sum # 进程要return,callback才可以拿到这个结果 if __name__ == '__main__':
start = datetime.datetime.now() # 注意一定要有这一句
pool = multiprocessing.Pool(4)
for i in range(4):
# 异步拿到的返回值是什么?
ret = pool.apply_async(calc, args=(i,))
print(ret, '~~~~~~~') # 异步,如何拿到真正的结果呢?
pool.close()
pool.join()
delta = (datetime.datetime.now() - start).total_seconds()
print(delta)
print('===end====')
<multiprocessing.pool.ApplyResult object at 0x000001F02D3230C8> ~~~~~~~
<multiprocessing.pool.ApplyResult object at 0x000001F02D3231C8> ~~~~~~~
<multiprocessing.pool.ApplyResult object at 0x000001F02D323308> ~~~~~~~
<multiprocessing.pool.ApplyResult object at 0x000001F02D3233C8> ~~~~~~~
5828 SpawnPoolWorker-4 7608 1000000000
7248 SpawnPoolWorker-3 5168 1000000000
2020 SpawnPoolWorker-1 2252 1000000000
1620 SpawnPoolWorker-2 3128 1000000000
47.058129
===end====

以上代码执行结果

4>.异步调用,拿最终结果

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import logging
import datetime
import multiprocessing # 日志打印进程id、进程名、线程id、线程名
logging.basicConfig(level=logging.INFO, format="%(process)d %(processName)s %(thread)d %(message)s") def calc(i):
sum = 0
for _ in range(1000000000): # 10亿
sum += 1
logging.info(sum)
return i, sum # 进程要return,callback才可以拿到这个结果 if __name__ == '__main__':
start = datetime.datetime.now() # 注意一定要有这一句 pool = multiprocessing.Pool(4) for i in range(4):
#异步拿到的返回值是什么?回调起了什么作用?
ret = pool.apply_async(calc, args=(i,),callback=lambda ret: logging.info('{} in callback'.format(ret)))
print(ret, '~~~~~~~') pool.close()
pool.join()
delta = (datetime.datetime.now() - start).total_seconds()
print(delta)
print('===end====')
<multiprocessing.pool.ApplyResult object at 0x0000017D9709F308> ~~~~~~~
<multiprocessing.pool.ApplyResult object at 0x0000017D9709F448> ~~~~~~~
<multiprocessing.pool.ApplyResult object at 0x0000017D9709F508> ~~~~~~~
<multiprocessing.pool.ApplyResult object at 0x0000017D9709F5C8> ~~~~~~~
8964 SpawnPoolWorker-1 1620 1000000000
572 MainProcess 8184 (0, 1000000000) in callback
992 SpawnPoolWorker-2 2484 1000000000
572 MainProcess 8184 (1, 1000000000) in callback
10608 SpawnPoolWorker-3 2404 1000000000
572 MainProcess 8184 (2, 1000000000) in callback
3268 SpawnPoolWorker-4 8684 1000000000
572 MainProcess 8184 (3, 1000000000) in callback
44.991332
===end====

以上代码执行结果戳这里

五.多进程、多线程的选择

1>.CPU密集型

  CPython中使用到了GIL,多线程的时候锁相互竞争,且多核优势不能发挥,选用Python多进程效率更高。

2>.IO密集型

  在Python中适合是用多线程,可以减少多进程间IO的序列化开销。且在IO等待的时候,切换到其他线程继续执行,效率不错。

3>.应用

请求/应答模型:WEB应用中常见的处理模型
  master启动多个worker工作进程,一般和CPU数目相同。发挥多核优势。
  worker工作进程中,往往需要操作网络IO和磁盘IO,启动多线程,提高并发处理能力。worker处理用户的请求,往往需要等待数据,处理完请求还要通过网络IO返回响应。
  这就是nginx工作模式。

六.Linux的特殊进程(在Linux/Unix中,通过父进程创建子进程。)

1>.僵尸进程

  一个进程使用了fork创建了子进程,如果子进程终止进入僵死状态,而父进程并没有调用wait或者waitpid获取子进程的状态信息,那么子进程仍留下一个数据结构保存在系统中,这种进程称为僵尸进程。

  僵尸进程会占用一定的内存空间,还占用了进程号,所以一定要避免大量的僵尸进程产生。有很多方法可以避免僵尸进程。

2>.孤儿进程

  父进程退出,而它的子进程仍在运行,那么这些子进程就会成为孤儿进程。孤儿进程会被init进程(进程号为1)收养,并由init进程对它们完成状态收集工作。

  init进程会循环调用wait这些孤儿进程,所以,孤儿进程没有什么危害。

3>.守护进程

  它是运行在后台的一种特殊进程。它独立于控制终端并周期性执行某种任务或等待处理某些事件。

  守护进程的父进程是init进程,因为其父进程已经故意被终止掉了。

  守护进程相对于普通的孤儿进程需要做一些特殊处理。

Python并发编程-多进程的更多相关文章

  1. python并发编程&多进程(二)

    前导理论知识见:python并发编程&多进程(一) 一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_cou ...

  2. python并发编程&多进程(一)

    本篇理论居多,实际操作见:  python并发编程&多进程(二) 一 什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 举例(单核+多道,实现多个进程的并发执行) ...

  3. python 并发编程 多进程 目录

    python multiprocessing模块 介绍 python 开启进程两种方法 python 并发编程 查看进程的id pid与父进程id ppid python 并发编程 多进程 Proce ...

  4. python 并发编程 多进程 队列目录

    python 并发编程 多进程 队列 python 并发编程 多进程 生产者消费者模型介绍 python 并发编程 多进程 生产者消费者模型总结 python 并发编程 多进程 JoinableQue ...

  5. python 并发编程 多进程 互斥锁 目录

    python 并发编程 多进程 互斥锁 模拟抢票 互斥锁与join区别

  6. python 并发编程 多进程 生产者消费者模型介绍

    一 生产者消费者模型介绍 为什么要使用生产者消费者模型 生产者指的是生产数据的任务,消费者指的是处理数据的任务, 生产数据目的,是为了给消费者处理. 在并发编程中,如果生产者处理速度很快,而消费者处理 ...

  7. python 并发编程-- 多进程

    一 multiprocessing 模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程 ...

  8. python 并发编程 多进程 互斥锁

    运行多进程  每个子进程的内存空间是互相隔离的 进程之间数据不能共享的 一 互斥锁 但是进程之间都是运行在一个操作系统上,进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终 ...

  9. Python并发编程-多进程socketserver简易版

    普通版的socketserver #server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080))#建立连接 sk.l ...

随机推荐

  1. EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器和EasyDSS云平台异同

    背景分析 不同于EasyDSS流媒体服务器与EasyDSS流媒体解决方案(EasyDSS流媒体解决方案就是通过EasyDSS流媒体服务器完善业务层研发而来),EasyDSS流媒体服务器和EasyDSS ...

  2. svn"重新定位"提示版本库uuid不匹配

    svn"重新定位"提示版本库 uuid不匹配: 版本库 "https://wolfcome110/svn/andon" 的 uuid是 "d52648 ...

  3. angular4.x实现一个全选,反选,外加从下一页返回上一页,选中上一次的操作记录

    productMap:any = new Map<string, string>(); //定义一个map的数据模型 //只要操作这个checkbox 那么只管把数据全部勾起了就行了 刷新 ...

  4. koa2 post请求ctx.request.body空获取不到的解决办法

    header请求头添加Content-type: application/json

  5. npm 的一些命令

    查看项目中是否安装某个插件 npm [name] -v [name] 为要查询的插件的名字,如果已经安装就会显示该插件的版本号 npm list 查看项目中所有已安装的插件

  6. mvn-dependencies-vs-dependencyManagement

    dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显式的声明需要用的依赖. dependencies 相对于dependencyManagement,所有声明在dep ...

  7. redis启动警告解决

    vim /etc/rc.localecho never > /sys/kernel/mm/transparent_hugepage/enabled加入上面那句到/etc/rc.local,开机启 ...

  8. appium学习笔记(环境安装配置,设备启动)

      Android SDK下载及配置 下载地址 下载后解压缩,打开SDK Manager.exe,下载适当版本的Android包 配置环境变量:目录下的tools路径(含uiautomatorview ...

  9. Consumer clientId=consumer-1, groupId=console-consumer-950] Connection to node -1 could not be

    kafka创建主题的ip地址最好用实际ip,不要用localhost https://blog.csdn.net/getyouwant/article/details/79000524

  10. Qt中 布局管理器失效问题

    1 问题描述 在Qt5.12.0 版本中,使用 自动管理器发生,无法生效 2 问题代码 Widget::Widget(QWidget *parent) : QWidget(parent), butto ...