1、进程简介
io密集型的尽量使用多线程,如socketserver.
multiprocessing模块可以使用一些简单的API批量生生进程,可以实现本地和远程进程间同步,通过使用子进程而不是线程来有效避开了全局解释器锁GIL,因此,该multiprocessing模块允许程序员在给定机器上充分利用多个处理器。它可以在Unix和Windows上运行。
一个简单的例子如下:
from multiprocessing import Process
def f(name):
print ‘hello’, name
if name == ‘main’:
p = Process(target=f, args=(‘bob’,))
p.start()
p.join()
为了显示所涉及的各个进程ID,下面是一个扩展的示例:
-- coding: utf-8 --
#@Author: kangdan
#@time :2019/12/12 10:42
#@Software:PyCharm Community Edition
from multiprocessing import Process
import os
def info(title):
print(title)
print(‘module name:’, name)
if hasattr(os, ‘getppid’): # only available on Unix
print(‘parent process:’, os.getppid())
print(‘process id:’, os.getpid())
def f(name):
info(‘function f’)
print(‘hello’, name)
if name == ‘main’:
info(‘main line’)
p = Process(target=f, args=(‘bob’,))
p.start()
p.join()
输出:
2、进程间通讯和数据共享
不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:
2.1、Queue队列
队列是线程和进程安全的。
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, ‘hello’])
if name == ‘main’:
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints “[42, None, ‘hello’]”
p.join()
不可以在主程序中通过将q设置为global方式在子进程中使用,因为不同进程间数据是不共享的!!!
2.2、Pipe管道(不常用,一般使用Queue)
Pipe()函数返回通过管道连接的一对连接对象,管道默认情况下为双工(双向)。例如:
from multiprocessing import Process, 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()代表管道的两端。每个连接对象都有send()和 recv()方法(以及其他方法)。请注意,如果两个进程(或线程)试图同时从管道的同一端读取或写入管道的同一端,则管道中的数据可能会损坏。当然,不存在同时使用管道不同端的过程造成损坏的风险。
2.3、Managers
由返回的管理器对象Manager()控制着一个服务器进程,该进程保存Python对象,并允许其他进程使用代理对其进行操作。
通过返回的经理Manager()将支持类型 list,dict,Namespace,Lock, RLock,Semaphore,BoundedSemaphore, Condition,Event,Barrier, Queue,Value和Array。例如,
from multiprocessing import Process, Manager
def f(d, l):
d[1] = ‘1’
d[‘2’] = 2
d[0.25] = None
l.reverse()
if name == ‘main’:
with Manager() as manager:
d = manager.dict()
l = manager.list(range(10))
p = Process(target=f, args=(d, l))
p.start()
p.join()
print(d)
print(l)
将打印
{0.25: None, 1: ‘1’, ‘2’: 2}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
服务器进程管理器比使用共享内存对象更灵活,因为可以使它们支持任意对象类型。同样,单个管理器可以由网络上不同计算机上的进程共享。但是,它们比使用共享内存慢。
2.4、Lock
非递归锁对象:模拟threading.Lock。一旦进程或线程获取了锁,则随后从任何进程或线程获取锁的尝试都将阻塞,直到释放为止;否则,该锁将被释放。任何进程或线程都可能释放它。
请注意,Lock实际上这是一个工厂函数,该函数返回multiprocessing.synchronize.Lock使用默认上下文初始化的实例。
Lock支持上下文管理器协议,因此可以在with语句中使用。
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()
2.5、Pool进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池有两种方法:
apply
apply_async
from multiprocessing import Pool,freeze_support
import time
def Foo(i):
time.sleep(2)
return i + 100
def Bar(arg):
print(’–>exec done:’, arg)
if name == ‘main’:
freeze_support()
pool = Pool(5)
for i in range(10):
pool.apply_async(func=Foo, args=(i,), callback=Bar)#异步
#callback回调函数,执行完Foo,会自动执行Bar,并且把Foo的返回值传给Bar参数
# pool.apply(func=Foo, args=(i,))#同步,没有callback
print('end')
pool.close()
pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。