python并发之multiprocessing
由于GIL(全局解释锁)的问题,python多线程并不能充分利用多核处理器。如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。multiprocessing可以给每个进程赋予单独的Python解释器,这样就规避了全局解释锁所带来的问题。与threading.Thread类似,可以利用multiprocessing.Process对象来创建一个进程。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
Process
单进程
# coding: utf-8
from multiprocessing import Process
import time
def task(msg):
print 'hello, %s' % msg
time.sleep(1)
if __name__ == '__main__':
p = Process(target=task, args=('world',))
p.start()
if p.is_alive():
print 'Process: %s is running' % p.pid
p.join()
这段代码的执行过程:在主进程中创建子进程,然后调用start()启动子进程,调用join()等待子进程执行完,再继续执行主进程的整个的执行流程。
控制子进程进入不同阶段的是 start(), join(), is_alive(), terminate(), exitcode() 方法,这些方法只能在创建子进程的进程中执行。
创建:创建进程需要一个 function 和相关参数,参数可以是dictProcess(target=func, args=(), kwargs = {}),name 可以用来标识进程。
关闭:close停止接收新的任务,如果还有任务来,就会抛出异常。 join 是等待所有任务完成。 join 必须要在 close 之后调用,否则会抛出异常。
等待:在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以在这里,我们调用了Process对象的join()方法 ,实际上等同于wait的作用。
对于多线程来说,由于只有一个进程,所以不存在此必要性。
结束:terminate() 结束工作进程,不再处理未完成的任务。
多进程
# coding: utf-8
from multiprocessing import Process
import multiprocessing
import time
def task1(msg):
print 'task1: hello, %s' % msg
time.sleep(1)
def task2(msg):
print 'task2: hello, %s' % msg
time.sleep(1)
def task3(msg):
print 'task3: hello, %s' % msg
time.sleep(1)
if __name__ == '__main__':
p1 = Process(target=task1, args=('one',))
p2 = Process(target=task2, args=('two',))
p3 = Process(target=task3, args=('three',))
start = time.time()
p1.start()
p2.start()
p3.start()
print("The number of CPU is:" + str(multiprocessing.cpu_count()))
for p in multiprocessing.active_children():
print("child p.name: " + p.name + "\tp.id: " + str(p.pid))
p1.join()
p2.join()
p3.join()
end = time.time()
print('3 processes take %s seconds' % (end - start))
执行结果:
task1: hello, one
task2: hello, two
task3: hello, three
The number of CPU is:4
child p.name: Process-1:p.id: 99359
child p.name: Process-2:p.id: 99360
child p.name: Process-3:p.id: 99361
3 processes take 1.00933504105 seconds
这里三个进程执行花费约1s,说明程序是并发执行的。对于更多的并发进程,我们可以放到一个循环中进行处理。
Lock
当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突
# coding: utf-8
from multiprocessing import Lock, Process
import time
def task1(lock, f):
with lock:
f = open(f, 'w+')
f.write('hello ')
time.sleep(1)
f.close()
def task2(lock, f):
lock.acquire()
try:
f = open(f, 'a+')
time.sleep(1)
f.write('world!')
except Exception as e:
print(e)
finally:
f.close()
lock.release()
if __name__ == '__main__':
lock = Lock()
fn = './file.txt'
start = time.time()
p1 = Process(target=task1, args=(lock, fn,))
p2 = Process(target=task2, args=(lock, fn,))
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time()
print 'time cost: %s seconds' % (end - start)
with open(fn, 'r') as f:
for x in f.readlines():
print x
执行结果:
time cost: 2.0059568882 seconds
hello world!
因为要访问共享文件,先获得锁的进程会阻塞后面的进程,因此程序运行耗时约2s。
Semaphore
Semaphore 和 Lock 稍有不同,Semaphore 相当于 N 把锁,获取其中一把就可以执行了。 信号量的总数 N 在构造时传入,s = Semaphore(N)。 和 Lock 一样,如果信号量为0,则进程堵塞,直到信号大于0。Semaphore可用来控制对共享资源的访问数量,例如池的最大连接数。
# coding: utf-8
from multiprocessing import Semaphore, Process
import time
def task(s, msg):
s.acquire()
print 'hello, %s' % msg
time.sleep(1)
s.release()
if __name__ == '__main__':
s = Semaphore(2)
processes = []
for x in range(8):
p = Process(target=task, args=(s, x,))
processes.append(p)
start = time.time()
for p in processes:
p.start()
for p in processes:
p.join()
end = time.time()
print '8 process takes %s seconds' % (end - start)
执行结果:
hello, 0
hello, 1
hello, 3
hello, 2
hello, 4
hello, 5
hello, 7
hello, 6
8 process takes 4.00831484795 seconds
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。这是Dijkstra信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器、文件这样的有限资源。
Event
Event用来实现进程间同步通信。
# coding: utf-8
from multiprocessing import Process, Event
import time
def task1(e, msg):
print 'task1 is waitting...'
e.wait()
time.sleep(1)
print 'hello, %s, e.is_set(): %s' % (msg, e.is_set())
def task2(e, msg):
print 'task2 is waitting...'
e.wait(msg)
print 'hello, %s, e.is_set(): %s' % (msg, e.is_set())
if __name__ == '__main__':
e = Event()
p1 = Process(target=task1, args=(e, 1))
p2 = Process(target=task2, args=(e, 2))
p1.start()
p2.start()
time.sleep(3)
e.set()
print 'main: event is set'
执行结果:
task1 is waitting...
task2 is waitting...
hello, 2, e.is_set(): False
main: event is set
hello, 1, e.is_set(): True
Pool
如果有50个task要执行,但 CPU 只有4核,我们当然可以循环创建50个进程来做这个事情,但这样处理大大增加进程管理和调度的开销。
如果可以只创建4个进程,让它们轮流工作完成任务,不用我们自己去管理具体的进程的创建、销毁和调度,岂不更好。multiprocessing中的 Pool 可以帮助我们做到这一点。
多进程异步
# coding: utf-8
from multiprocessing import Pool
import time
def task(msg):
print 'hello, %s' % msg
time.sleep(1)
if __name__ == '__main__':
pool = Pool(processes=4)
for x in range(10):
pool.apply_async(task, args=(x,))
pool.close()
pool.join()
print 'processes done.'
注意:这里使用的是apply_async,多个进程异步执行;如果调用async,就变成阻塞版本了。
执行结果:
hello, 0
hello, 1
hello, 2
hello, 3
hello, 4
hello, 5
hello, 6
hello, 7
hello, 8
hello, 9
10 processes take 3.09226489067 seconds
Pool 进程池创建4个进程,不管有没有任务,都一直在进程池中等候。官网的描述:Worker processes within a Pool typically live for the complete duration of the Pool’s work queue。数据来的时候,若有空闲进程,则利用空闲的进程完成任务,直到所有任务完成为止;若没有空闲的进程,则需要等待,直到池中有进程结束。
获取进程执行结果
更多的时候,我们不仅需要多进程执行,还需要关注每个进程的执行结果,我们可以通过获取apply_async的返回值得到执行结果。
# coding: utf-8
from multiprocessing import Pool
import time
def task(msg):
print 'hello, %s' % msg
time.sleep(1)
return 'msg: %s' % msg
if __name__ == '__main__':
pool = Pool(processes=4)
results = []
for x in range(10):
ret = pool.apply_async(task, args=(x,))
results.append(ret)
pool.close()
pool.join()
print 'processes done, result:'
for x in results:
print x.get()
pool中的map方法
上面我们是通过一个循环往进程池添加任务,Pool提供了更优雅的map方法来管理任务的提交,只需对上面的代码稍作修改。
# coding: utf-8
from multiprocessing import Pool
import time
def task(msg):
print 'hello, %s' % msg
time.sleep(1)
return 'msg: %s' % msg
if __name__ == '__main__':
pool = Pool(processes=4)
results = []
msgs = [x for x in range(10)]
results = pool.map(task, msgs)
pool.close()
pool.join()
print 'processes done, result:'
for x in results:
print x
python并发之multiprocessing的更多相关文章
- Python中的multiprocessing和threading
Python中的multiprocessing和threading分别使用来实现多进程编程和多线程编程的.其中threading比较简单,而前者比较繁琐. 下面,我们进行一下分析: 多线程--thre ...
- Python多进程并发(multiprocessing)用法实例详解
http://www.jb51.net/article/67116.htm 本文实例讲述了Python多进程并发(multiprocessing)用法.分享给大家供大家参考.具体分析如下: 由于Pyt ...
- python开发之路:python数据类型(老王版)
python开发之路:python数据类型 你辞职当了某类似微博的社交网站的底层python开发主管,官还算高. 一次老板让你编写一个登陆的程序.咔嚓,编出来了.执行一看,我的妈,报错? 这次你又让媳 ...
- Python多进程库multiprocessing中进程池Pool类的使用[转]
from:http://blog.csdn.net/jinping_shi/article/details/52433867 Python多进程库multiprocessing中进程池Pool类的使用 ...
- python之多进程multiprocessing模块
process类介绍 multiprocessing 模块官方说明文档 Process 类用来描述一个进程对象.创建子进程的时候,只需要传入一个执行函数和函数的参数即可完成 Process 示例的创建 ...
- python学习笔记——multiprocessing 多进程组件 进程池Pool
1 进程池Pool基本概述 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量时间,如果操作的对象数目不大时,还可以直接适用Process类动态生成 ...
- Python3.6编译安装以及python开发之virtualenv与virtualenvwrapper
Python3.6编译安装 下载python源码包 先到安装目录 cd /opt 下载源码包 wget https://www.python.org/ftp/python/3.6.2/Python-3 ...
- python 并发之多进程实现
一.multipricessing模块的介绍 python中的多线程无法利用多核优势,如果想要充分的使用多核CPU资源,在python中大部分情况下需要用多线程,python提供了multiproce ...
- python并发之多进程
#mutiprocessing模块 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.Pytho ...
随机推荐
- 【51nod-1183】编辑距离
链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1183 #include <bits/stdc++.h> ...
- Prism开发人员指南5-WPF开发 文档翻译(纯汉语版)
2014四月 Prism以示例和文档的形式帮助你更简单的设计丰富灵活易维护的WPF程序.其中使用的设计模式体现了一些重要的设计原则,例如分离关注点和松耦合,Prism帮助你利用松耦合组件设 ...
- wc.exe(c语言实现)
Github项目地址:https://github.com/zhongciting2009/wc WC 项目要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数.这个项目要求写 ...
- java事务(二)——本地事务
本地事务 事务类型 事务可以分为本地事务和分布式事务两种类型.这两种事务类型是根据访问并更新的数据资源的多少来进行区分的.本地事务是在单个数据源上进行数据的访问和更新,而分布式事务是跨越多个数据源来进 ...
- 专业工具软件PCB板打印说明
专业工具软件PCB板打印说明 请注意PCB板打印不要直接截图,如下方式是不正确的: 这样在打印为黑白图片时,元器件之间的连线无法看清. 应采用如下模式: ~End~
- Kali Linux安装SSH Server
Kali Linux默认并没有安装SSH服务,为了实现远程登录Kali Linux,我们需要安装SSH服务. 安装 OpenSSH Server # apt-get install openssh-s ...
- MpVue开发之swiper的使用
用到的关键字如下: class :class current :current bindchange @change circular 是否实现无限滑动 true/false skip-hidden ...
- 深入分析AIDL原理
深入分析AIDL原理 分类: Android2011-11-18 17:29 6522人阅读 评论(1) 收藏 举报 descriptorcallbackservicenullinterfaceser ...
- element resetFields 方法报错
resetFields 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果:如果用了之后报错如下: 查了下资料,是DOM加载的顺序问题: 解决方法: this.$nextTick(() =& ...
- spring_JavaConfig
从Spring 3起,JavaConfig功能已经包含在Spring核心模块,它允许开发者将bean定义和在Spring配置XML文件到Java类中. interface: package sprin ...