Python3 多进程 (multiprocessing)
要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。
Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。
multiprocessing
如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?
由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。
multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:
Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:
Unix/Linux操作系统
#multiprocessing.py
import os
print 'Process (%s) start...' % os.getpid()
pid = os.fork()
if pid==0:
print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())
else:
print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)
win 操作系统
from multiprocessing import Process
import os
#子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Process will start.')
p.start()
p.join()
print('Process end.')
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid()
p = Pool()
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
代码解读:
对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。
请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:
p = Pool(5)
就可以同时跑5个进程。
由于Pool的默认大小是CPU的核数,如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。
多进程
import multiprocessing as np
import time
def run(name):
time.sleep(2)
print('heelo', name)
if __name__ == '__main__':
for i in range(10): # 起了10个进程
p = np.Process(target=run, args=('bob%s' % i,))
p.start()
多进程 多线程 运用
import multiprocessing
import time, threading
def thread_run():
print(threading.get_ident()) # get_ident获取当前线程id
def run(name):
time.sleep(2)
print('heelo', name)
t = threading.Thread(target=thread_run, ) # 在每个进程中又起了1个线程
t.start()
if __name__ == '__main__':
for i in range(10): # 起了10个进程
p = multiprocessing.Process(target=run, args=('bob%s' % i,))
p.start()
进程池
执行多进程,子进程会从主进程复制一份完整数据,1个、10个进程可能还没什么感觉,但是如果有100或1000,甚至更多个进程的时候开销就会特别大,就会明显感觉到多进程执行有卡顿现象。
进程池可以设定同一时间有多少个进程可以在CPU上运行。
from multiprocessing import Process, Pool
#从multiprocessing导入pool
import time,os
def Foo(i):
time.sleep(2)
print("in process",os.getpid()) #打印进程id
return i + 100
def Bar(arg):
print('-->exec done:', arg)
if __name__ == '__main__': ##这行代码用途是如果主动执行该代码的.py文件,则该代码下面的代码可以被执行;如果该.py模块被导入到其他模块中,从其他模块执行该.py模块,则该行下面的代码不会被执行。 有些时候可以用这种方式用于测试,在该行代码下面写一些测试代码。。
pool = Pool(5) #同时只能放入5个进程
for i in range(10): #创建10个进程,但是因为pool的限制,只有放入进程池中的5个进程才会被执行(),其他的被挂起了,如果进程池中其中有两个进程执行完了,就会补进2个进程进去。
# pool.apply_async(func=Foo, args=(i,), callback=Bar)
pool.apply(func=Foo, args=(i,)) #pool.apply用来将进程放入pool
print('end') #执行完毕
pool.close() #允许pool中的进程关闭(close必须在join前面,可以理解close相当于一个开关吧)
pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
执行结果:
进程同步
在进程里面也有锁
from multiprocessing import Process, Lock
#从multiprocessing导入Lock这个锁
def f(l, i):
l.acquire() #获取修改数据的锁
print('hello world', i)
l.release() #释放锁
if __name__ == '__main__':
lock = Lock() #实例锁
for num in range(10): #生成10个进程
Process(target=f, args=(lock, num)).start() #执行子进程并传入参数给子进程
进程间通讯
默认进程之间数据是不共享的,如果一定要实现互访可以通过Queue来实现,这个Queue和线程中的Queue使用方法一样,不过线程中的Queue只能在线程之间使用。
import queue
import threading
def f():
q.put([42,None,'heelo'])
if __name__ == '__main__':
q = queue.Queue()
p = threading.Thread(target=f,)
p.start()
print (q.get())
p.join()
执行结果:
[42, None, 'heelo']
Python3.x:实现多任务(多进程)
# python3
# author lizm
# datetime 2018-02-13 16:00:00
# -*- coding: utf-8 -*-
#引用xyzq_shrgp 文件的doStartShrgp函数
from xyzq_shrgp import doStartShrgp
from xyzq_shrjj import doStartShrjj
from xyzq_szzjtj import doStartSzzjtj
from datetime import datetime
import datetime, time
import sys
import logging
import configparser
import threading
logger = logging.getLogger()
#set loghandler
file = logging.FileHandler("yzzq_log"+time.strftime("%Y%m%d")+".log")
logger.addHandler(file)
#set formater
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
file.setFormatter(formatter)
#set log level
logger.setLevel(logging.NOTSET)
#调用xyzq_shrgp 文件的doStartShrgp函数
def doShrgp(date):
doStartShrgp(date)
def doShrjj(date):
doStartShrjj(date)
def doSzzjtj(date):
doStartSzzjtj(date)
#测试
if __name__ == '__main__':
if len(sys.argv) < 2:
logger.info("传递参数错误,日期参数[格式:20180122]")
print("传递参数错误,日期参数[格式:20180122]")
else:
vrg_date = sys.argv[1]
if len(vrg_date) ==8:
#加载任务进程(注意:参数数以','结尾)
th_shrgp = threading.Thread(target=doShrgp,args=(vrg_date,))
#加载任务进程(注意:参数以','结尾)
th_shrjj = threading.Thread(target=doShrjj,args=(vrg_date,))
#加载任务进程(注意:参数以','结尾)
th_szzjtj = threading.Thread(target=doSzzjtj,args=(vrg_date,))
#开始任务(并发执行)
th_shrgp.start()
th_shrjj.start()
th_szzjtj.start()
else:
logger.info("日期参数格式不正确,请用格式:20180116")
print("日期参数格式不正确,请用格式:20180116")
本文介绍了Python3的multiprocessing模块,详细讲解了如何在Unix/Linux和Windows操作系统上实现多进程,包括Process类的使用、子进程的创建、进程池的管理以及进程同步和通讯。通过实例展示了Pool的用法,探讨了多进程与多线程的运用,并讨论了进程同步的重要性。

3525

被折叠的 条评论
为什么被折叠?



