python多进程与多线程(二):使用多进程

什么是进程?

额,进程呢就是你运行一个程序,这时他就启动了一个进程。。。(真的没啥好说的了,)


不同进程的关系

每一个进程都有自己的资源,并且是不共享的,就像你家的鸡不会送给邻居家吃(特例除外。。。)

子进程的资源是将主进程的资源进行了拷贝,虽然你操作的变量名字一样,但是眼前人已非彼时人,最后输出,唯有失望!


如何使用多进程

python使用多进程一共有三个包可以用,因为multiprocessing这个包兼容性好,所以我们用它来做介绍。

要注意,在Windows环境下要将主进程代码写在if __name__=='__main__':下面

因为操作系统问题,Windows系统会把后面的代码也拷贝,导致形成无限递归,会一直报错,但是写在if __name__=='__main__':下面之后,系统会自动判断。


多进程开始

我们可以试使用multiprocessing包中的Process方法创建多进程

from multiprocessing import Process
def process1(x):
    print(x)
    for i in range(1,4):
        print(i,'*'*15,1)
def process2(x):
    print(x)
    for i in range(5,8):
        print(i,'*'*15,2)

if __name__ == '__main__':
    first = Process(target = process1,args=(1,))
    second = Process(target = process2,args=(2,))
    first.start()
    second.start()

锵锵锵,我们在主进程里面创建了两个子进程哦。

在这里我对代码做一个解释:

  • Process表示创建一个进程
  • target表示进程要调用的函数,注意不要写小括号,写了的话就成了实现函数了,不是参数了
  • args表示传入函数的参数,当参数只有一个的时候要在最后面写上逗号,我这里只是为了体现有这个功能才写的参数x
  • start表示进程开始执行,但各个进程的执行顺序不是按代码执行的,是cpu分配的,可能每次和每次都不同

代码加上个join

from multiprocessing import Process
def process1(x):
    print(x)
    for i in range(1,4):
        print(i,'*'*15,1)
def process2(x):
    print(x)
    for i in range(5,8):
        print(i,'*'*15,2)

if __name__ == '__main__':
    first = Process(target = process1,args=(1,))
    second = Process(target = process2,args=(2,))
    first.start()
    first.join
    second.start()

这个代码不管你执行多少次,都是先执行process1,然后执行process2。这是join函数的作用。

我们使用进程.join(),就表示告诉主进程,等这个子进程执行完了再干别的事


join、start、run都是什么意思

start方法中包含run方法,其实写run和start的效果都是差不多的。

join的意思是当前进程执行完再执行别的进程,通常用于进程同步。


多进程不能同步全局变量

我们重新写两个进程,一个修改全局变量,一个输出全局变量,看看是什么效果

from multiprocessing import Process

x = 10

def process1():
    global x
    for i in range(1,4):
        x += 1
    print(x,'process1')

def process2():
    global x
    print(x,'process2')  # 看看能不能得到process1处理后的x
    x -=1
    print(x,'process2')


if __name__ == '__main__':
    first = Process(target = process1)
    second = Process(target = process2)
    first.start()
    first.join()
    second.start()
    print(x,'main')  # 判断全局变量有没有被改变

这是因为,每个进程都是将主进程的资源copy了一份,对本进程的变量进行修改,不是对主进程的变量进行修改!


如何使用多进程同步全局变量

可以试用管道或队列的方法,在这里,推荐使用队列的方法,因为队列方便操作。

队列是一种先进先出的数据结构,python的队列可以放置任意数据类型,常用函数如下

函数描述
q.empty()判断队列是否为空
q.put()在队列末尾添加一个元素
q.get()取出队列第一个元素

我们在传参数的时候,要将队列作为参数一起传进去,修改后的代码如下:

from multiprocessing import Process,Queue

def process1(q):
    for i in range(3):
        q.put(i)

def process2(q):
    while not q.empty():
        print(q.get())

if __name__ == '__main__':
    q = Queue(5)  # 表示队列最多放五个元素
    first = Process(target = process1,args = (q,))
    second = Process(target = process2,args = (q,))
    first.start()
    first.join()
    second.start()

我们惊奇的发现,全局变量同步了诶hiahiahia,这是因为队列是写在内存上的哦


进程池简介

当我们想批量执行一个函数,又不想去用列表创建的时候,或者是异步执行的的时候,我们就可以使用进程池。

进程池进程的最大数量不能超过你cpu内核,一般来说4个进程就可以了,你可以使用下面的代码看看你是多少核的cpu

import os
print("本机为",os.cpu_count(),"核 CPU")  # 本机为4核

流量池的使用步骤如下:

  • 创建流量池
  • apply_async添加异步函数或者apply添加同步函数
  • 调用close()方法来停止添加函数
  • join()开始执行调用流量池

使用进程池同步调用函数

from multiprocessing import Pool
import time

def func(msg):
    print( "msg:", msg)
    time.sleep(0.1)
    return msg

if __name__ == "__main__":
    pool = Pool(processes = 3)
    res_l=[]
    for i in range(10):
        msg = "hello %d" %(i)
        res = pool.apply(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        res_l.append(res) #同步执行,即执行完一个拿到结果,再去执行另外一个
    print("==============================>")
    pool.close()
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

    print(res_l) #看到的就是最终的结果组成的列表
    for i in res_l: #apply是同步的,所以直接得到结果,没有get()方法
        print(i)

需要强调的是:此操作并不会在所有池工作进程中并执行func函数。

如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()


使用进程池异步调用函数

from multiprocessing import Pool
import time

def func(msg):
    print( "msg:", msg)
    return msg

if __name__ == "__main__":
    pool = Pool(processes = 3)
    res_l=[]
    for i in range(10):
        msg = "hello %d" %(i)
        res = pool.apply_async(func, (msg, ))
        res_l.append(res)
    print("==============================>")

    pool.close() #关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

    print(res_l) #看到的是<multiprocessing.pool.ApplyResult object at 0x10357c4e0>对象组成的列表,而非最终的结果,但这一步是在join后执行的,证明结果已经计算完毕,剩下的事情就是调用每个对象下的get方法去获取结果
    for i in res_l:
        print(i.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get

这个程序就代表,同一时间,有四个进程在跑这个函数


如何用进程池使用队列

使用流量池同步数据,也是使用队列,但是这里的队列来源不同

from multiprocessing import Manager
q = Manager.Queen(5)

其他操作就没啥了,把q也作为传到函数里就好了


如何获取进程编号

os库提供了获取进程编号的相关方法。

函数描述
os.getpid()获取当前进程的编号
os.getppid()获取父进程的编号

特别要注意的地方

要根据情况慎用join,因为稍不留意,你的多进程(线程)程序效果还不如单进程(线程)。

因为如果你总是等一个进程执行完再去执行另外一个进程,可能执行时间比单进程还要长,建议大家使用流量池异步执行或者不写join

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笼中小夜莺

嘿嘿嘿,请用金钱尽情地蹂躏我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值