Python3 多进程 使用情况

本文介绍了Python3的multiprocessing模块,详细讲解了如何在Unix/Linux和Windows操作系统上实现多进程,包括Process类的使用、子进程的创建、进程池的管理以及进程同步和通讯。通过实例展示了Pool的用法,探讨了多进程与多线程的运用,并讨论了进程同步的重要性。

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")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值