热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:Python多进程编程

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python多进程编程相关的知识,希望对你有一定的参考价值。一 多进程编程

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python多进程编程相关的知识,希望对你有一定的参考价值。



一 多进程编程

Python实现多进程的方式有两种:一种方法是os模块中的fork方法,另一种是使用multiprocessing模块。

前者仅适用于LINUX/UNIX操作系统,对Windows不支持,后者则是跨平台的实现方式。

第一种方式:使用os模块中的fork方式实现多进程


import os
if __name__ == \'__main__\':
print \'current Process (%s) start ...\'%(os.getpid())
pid
= os.fork()
if pid < 0:
print \'error in fork\'
elif pid == 0:
print \'I am child process(%s) and my parent process is (%s)\',(os.getpid(),os.getppid())
else:
print \'I(%s) created a chlid process (%s).\',(os.getpid(),pid)

第二种方式:multiprocessing

由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。



  • 在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。

  • multiprocessing提供了threading包中没有的IPC(比如Pipe和Queue),效率上更高。应优先考虑Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因为它们占据的不是用户进程的资源)。

  • 多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。

Process.PID中保存有PID,如果进程还没有start(),则PID为None。

window系统下,需要注意的是要想启动一个子进程,必须加上那句if __name__ == "main",进程相关的要写在这句下面。

创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()启动某个进程。join()方法实现进程间的同步。


#__author: greg
#
date: 2017/9/19 23:52
from multiprocessing import Process
import time
def f(name):
time.sleep(
1)
print(\'hello\', name,time.ctime())
if __name__ == \'__main__\':
p_list
=[]
for i in range(3):
p
= Process(target=f, args=(\'alvin\',))
p_list.append(p)
p.start()
for i in p_list:
i.join()
print(\'end\')#一个主进程,三个子进程
# output:
#
hello alvin Fri Nov 24 19:10:08 2017
#
hello alvin Fri Nov 24 19:10:08 2017
#
hello alvin Fri Nov 24 19:10:08 2017
#
end

类式调用:


#__author: greg
#
date: 2017/9/21 20:02
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self):
super(MyProcess, self).
__init__()
#self.name = name
def run(self):
time.sleep(
1)
print (\'hello\', self.name,time.ctime())
if __name__ == \'__main__\':
p_list
=[]
for i in range(3):
p
= MyProcess()
p.start()
p_list.append(p)
for p in p_list:
p.join()
print(\'end\')
#output:
#
hello MyProcess-1 Fri Nov 24 19:12:17 2017
#
hello MyProcess-2 Fri Nov 24 19:12:17 2017
#
hello MyProcess-3 Fri Nov 24 19:12:17 2017
#
end

显示进程ID号:


#__author: greg
#
date: 2017/9/21 20:16
from multiprocessing import Process
import os
import time
def info(title):
print(title)
print(\'module name:\', __name__)
print(\'parent process:\', os.getppid())#父进程号
print(\'process id:\', os.getpid())#进程号
def f(name):
info(
\'\\033[31;1mfunction f\\033[0m\')
print(\'hello\', name)
if __name__ == \'__main__\':
info(
\'\\033[32;1mmain process line\\033[0m\')
time.sleep(
10)
p
= Process(target=info, args=(\'bob\',))
p.start()
p.join()
#output:
#
main process line
#
module name: __main__
#
parent process: 1548 pycharm的进程号
#
process id: 8416 Python进程号
#
bob
#
module name: __mp_main__
#
parent process: 8416 Python进程号
#
process id: 5556 info进程号


二 Process类

构造方法:

Process([group [, target [, name [, args [, kwargs]]]]])

  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 进程名; 
  args/kwargs: 要传入方法的参数。

实例方法:

  is_alive():返回进程是否在运行。

  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

  start():进程准备就绪,等待CPU调度

  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。

  terminate():不管任务是否完成,立即停止工作进程

属性:

  authkey

  daemon:和线程的setDeamon功能一样

  exitcode(进程在运行时为None、如果为–N,表示被信号N结束)

  name:进程名字。

  pid:进程号。

三 进程间通讯

不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:

Queues 用来在多个进程间通信

1. 阻塞模式


import queue
import time
q
= queue.Queue(10) #创建一个队列
start=time.time()
for i in range(10):
q.put(
\'A\')
time.sleep(
0.5)
end
=time.time()
print(end-start)

这是一段极其简单的代码(另有两个线程也在操作队列q),我期望每隔0.5秒写一个\'A\'到队列中,但总是不能如愿:
间隔时间有时会远远超过0.5秒。
原来,Queue.put()默认有 block = True 和 timeout两个参数。
源码:def put(self, item, block=True, timeout=None):
当 block = True 时,写入是阻塞式的,阻塞时间由 timeout确定。
当队列q被(其他线程)写满后,这段代码就会阻塞,直至其他线程取走数据。
Queue.put()方法加上 block=False 的参数,即可解决这个隐蔽的问题。
但要注意,非阻塞方式写队列,当队列满时会抛出 exception Queue.Full 的异常。


#__author: greg
#
date: 2017/9/21 22:27
from multiprocessing import Process, Queue
def f(q,n):
q.put([
42, n, \'hello\'])
print(\'subprocess id\',id(q))
if __name__ == \'__main__\':
q
= Queue()
p_list
=[]
print(\'process id\',id(q))
for i in range(3):
p
= Process(target=f, args=(q,i))
p_list.append(p)
p.start()
print(q.get())
print(q.get())
print(q.get())
for i in p_list:
i.join()
# output
#
process id 2284856854176
#
subprocess id 2607348001872
#
[42, 0, \'hello\']
#
subprocess id 1712786975824
#
[42, 2, \'hello\']
#
subprocess id 2254764977120
#
[42, 1, \'hello\']

Pipe常用来两个进程间进行通信,两个进程分别位于管道的两端

 


def f(conn):
conn.send([
42, None, \'hello\'])
conn.close()
if __name__ == \'__main__\':
parent_conn, child_conn
= Pipe()
p
= Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, \'hello\']"
p.join()

 

 

Pipe方法返回(conn1, conn2)代表一个管道的两个端。Pipe方法有duplex参数,如果duplex参数为True(默认值),那么这个管道是全双工模式,也就是说conn1和conn2均可收发。duplex为False,conn1只负责接受消息,conn2只负责发送消息。

 


send和recv方法分别是发送和接受消息的方法。例如,在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。 


#__author: greg
#
date: 2017/9/21 22:57
import multiprocessing
import random
import time,os
def proc_send(pipe,urls):
for url in urls:
print("Process(%s) send: %s" %(os.getpid(),url))
pipe.send(url)
time.sleep(random.random())
def proc_recv(pipe):
while True:
print("Process(%s) rev:%s" %(os.getpid(),pipe.recv()))
time.sleep(random.random())
if __name__=="__main__":
pipe
=multiprocessing.Pipe()
p1
=multiprocessing.Process(target=proc_send,args=(pipe[0],[\'url_\'+str(i)
for i in range(10)]))
p2
=multiprocessing.Process(target=proc_recv,args=(pipe[1],))
p1.start()
p2.start()
p1.join()
p2.terminate()

Manager()返回的管理器对象控制一个服务器进程,该进程持有Python对象,并允许其他进程使用代理来操纵它们。


#__author: greg
#
date: 2017/9/21 23:10
from multiprocessing import Process, Manager
def f(d, l,n):
d[n]
= \'1\'
d[
\'2\'] = 2
d[
0.25] = None
l.append(n)
# print(l)
if __name__ == \'__main__\':
with Manager() as manager:
d
= manager.dict()
l
= manager.list(range(5))
p_list
= []
for i in range(10):
p
= Process(target=f, args=(d, l,i))
p.start()
p_list.append(p)
for res in p_list:
res.join()
print(d)
print(l)

四 进程同步

当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突。


#__author: greg
#
date: 2017/9/21 23:25
from multiprocessing import Process, Lock
def f(l, i):
l.acquire()
try:
print(\'hello world\', i)
finally:
l.release()
if __name__ == \'__main__\':
lock
= Lock()
for num in range(10):
Process(target
=f, args=(lock, num)).start()

五 进程池 Pool类

Pool可以提供指定数量的进程供用户使用,默认大小是CPU的核数。当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程来执行该请求

但如果池中的进程数已经达到规定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来处理它。


# -*- coding: utf-8 -*-
#
2017/11/24 20:15
from multiprocessing import Pool
import os, time, random
def run_task(name):
print(\'Task %s (pid = %s) is running...\' % (name, os.getpid()))
time.sleep(random.random()
* 3)
print(\'Task %s end.\' % name)
if __name__==\'__main__\':
print(\'Current process %s.\' % os.getpid())
p
= Pool(processes=3)
for i in range(5):
p.apply_async(run_task, args
=(i,))
print(\'Waiting for all subprocesses done...\')
p.close()
p.join()
print(\'All subprocesses done.\')
"""
Current process 9788.
Waiting for all subprocesses done...
Task 0 (pid = 5916) is running...
Task 1 (pid = 3740) is running...
Task 2 (pid = 6964) is running...
Task 2 end.
Task 3 (pid = 6964) is running...
Task 1 end.
Task 4 (pid = 3740) is running...
Task 0 end.
Task 3 end.
Task 4 end.
All subprocesses done.
"""



  • apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞

  • close()    关闭pool,使其不在接受新的任务。

  • terminate()    结束工作进程,不在处理未完成的任务。

  • join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。

每次最多运行3个进程,当一个任务结束了,新的任务依次添加进来,任务执行使用的进程依然是原来的进程,这一点通过进程的pid可以看出来。

 


 



推荐阅读
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
  • 文件路径的生成及其在文件操作中的应用
    本文介绍了文件路径的生成方法及其在文件操作中的应用。在进行文件操作时,需要知道文件的具体位置才能打开文件。文件的位置有绝对路径和相对路径之分。绝对路径通常只在特定电脑上有效,不同电脑上的文件存放路径可能不同,导致程序报错。相对路径是解决这个问题的最好方式,它不依赖于文件的具体存放位置,只需要按照统一的规范进行文件存放即可。使用相对路径可以避免冗余和麻烦,特别适用于大项目和团队维护代码的情况。 ... [详细]
author-avatar
zhenhuaYang
编程、骑行、健身、民谣、生活!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有