Python并发编程-多进程
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并发编程-多进程的更多相关文章
- python并发编程&多进程(二)
前导理论知识见:python并发编程&多进程(一) 一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_cou ...
- python并发编程&多进程(一)
本篇理论居多,实际操作见: python并发编程&多进程(二) 一 什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 举例(单核+多道,实现多个进程的并发执行) ...
- python 并发编程 多进程 目录
python multiprocessing模块 介绍 python 开启进程两种方法 python 并发编程 查看进程的id pid与父进程id ppid python 并发编程 多进程 Proce ...
- python 并发编程 多进程 队列目录
python 并发编程 多进程 队列 python 并发编程 多进程 生产者消费者模型介绍 python 并发编程 多进程 生产者消费者模型总结 python 并发编程 多进程 JoinableQue ...
- python 并发编程 多进程 互斥锁 目录
python 并发编程 多进程 互斥锁 模拟抢票 互斥锁与join区别
- python 并发编程 多进程 生产者消费者模型介绍
一 生产者消费者模型介绍 为什么要使用生产者消费者模型 生产者指的是生产数据的任务,消费者指的是处理数据的任务, 生产数据目的,是为了给消费者处理. 在并发编程中,如果生产者处理速度很快,而消费者处理 ...
- python 并发编程-- 多进程
一 multiprocessing 模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程 ...
- python 并发编程 多进程 互斥锁
运行多进程 每个子进程的内存空间是互相隔离的 进程之间数据不能共享的 一 互斥锁 但是进程之间都是运行在一个操作系统上,进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终 ...
- Python并发编程-多进程socketserver简易版
普通版的socketserver #server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080))#建立连接 sk.l ...
随机推荐
- [转]Mathjax语法总结
链接地址:https://blog.csdn.net/ajacker/article/details/80301378
- CentOS升级kernel
CentOS升级kernel 升级命令: yum update kernel yum update kernel-devel yum update kernel-firmware yum update ...
- scala 样例类
一.case class 的特征 package com.jason.qianfeng case class Message(sender: String, receiver: String, bod ...
- 推荐一款好用的json导出execl格式的文件的js工具-JsonExportExcel
<html> <head> <meta charset="utf-8"> <title>json导出Excel</title& ...
- npm与yarn命令
npm 1. 查看npm版本 node -v npm -v 2. 更新npm至最新版 npm install npm@latest -g 3. npm install:安装依赖 # 在本地node_m ...
- dp --- acdream原创群赛(16) --- B - Apple
<传送门> B - Apple Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Other ...
- Word页眉实现首页不同、奇偶页不同 、更改页眉横线、页眉文字对齐 -- 视频教程(8)
1. 目标 目标1:实现页眉"首页不同,奇偶页不同" 目标2:更改页眉横线 目标3:页眉文字有三部分:第一部分左对齐,第二部分居中,第三部分右对齐 2. 教程 未完 ...... ...
- Delphi RSA签名与验签【支持SHA1WithRSA(RSA1)、SHA256WithRSA(RSA2)和MD5WithRSA签名与验签】
作者QQ:(648437169) 点击下载➨ RSA签名与验签 [delphi RSA签名与验签]支持3种方式签名与验签(SHA1WithRSA(RSA1).SHA256WithRSA(RSA2)和M ...
- redis 设置自启动
redis 设置自启动 1.创建服务(redis.conf 配置文件要注意,经过cp产生了很多个redis.conf) vim /lib/systemd/system/redis.service [U ...
- AJAX调用数据,滚动到底部
最近一个小项目里面,需要使用AJAX去拉取数据,并且直接显示最后一条信息,也就是滚动到底部.实现脚本如下: var scrollHeight = $('.txtBox3').prop("sc ...