python多进程全方位总结

简单聊聊多进程 

    关于多进程,简单来说每一个程序就可以看成是一个进程,如:QQ,微信,百度地图,我们能在手机或电脑等终端同时使用它们,是因为多个应用程序在CPU上高速切换,由于切换速度过快导致人的肉眼无法察觉。因此我们会认为程序是在同时运行,但是实际上并不是,这是电脑处理速度快给人造成的一种感觉。有时候电脑会出现卡顿这是因为CPU处理不过来导致的。

单核CPU如何实现多任务

    假设只有一个CPU,现在有四个进程,分别是QQ,微信,默默,探探。这个时候四个进程在CPU的门口等着进入CPU当中执行。每一个进程执行很小的一段时间,然后换另一个进程来执行,这样就可以实现多任务。

    如果这个时候有四个CPU的话,他们就可以分别在四个CPU中处理。

    显然四核CPU的处理速度比单核的要快。

关于并发和并行

    并发就是看上去好像是同时执行,但是实际上不是(时间片的轮询),这种情况发生在任务数多于CPU的时候,大多数情况下是这样。

    并行是真正的一起执行,这样的速度是最快的,但是在现实中一般不会实现,这需要CPU数大于等于任务数。

进程

    进程是程序执行和资源分配的基本单位,每个进程都有自己的数据、代码和堆栈。

关于如何查看子进程和父进程号

    可以导入os模块,通过os.getpid()获得当前进程号,然后通过os.getppid()获得子进程的父进程号。

 

 

所谓多进程就是同时进行多件事,就比如我们平时一边吃饭,一边看电视。吃饭10分钟,看电视十分钟,如果先吃完饭再看电视的话就需要20分钟。但是两件事情同时执行的话,就只需10分钟即可。

代码实现如下:

from multiprocessing import Process
import time,random
def eat(name):
    time.sleep(1)
    print("%s在吃饭"%(name))

def watchTV(name):
    time.sleep(0.5)
    print("%s在看电视"%(name))
if __name__ == '__main__':
    p1 = Process(target=eat,args=("楼下小黑",))
    p2 = Process(target=watchTV,args=("楼下小黑",))
    p1.start()
    p2.start()

结果为:

楼下小黑在看电视
楼下小黑在吃饭 

 

 

插入:关于父子进程结束先后问题

    如下代码,父进程执行完成之后,子进程再执行完。父进程不会等待子进程执行完成后再执行父进程的结束操作。

from multiprocessing import Process
import time
def test():
    print("子进程开始执行")
    time.sleep(2)
    print("子进程结束")

if __name__ == '__main__':
    print("父进程开始执行")
    p = Process(target=test)
    p.start()
    print("父进程结束")
"""
结果为:
父进程开始执行
父进程结束
子进程开始执行
子进程结束
"""

    为了让父进程等待子进程执行完成之后再执行,这个时候我们需要用到join()。在p.start()之后加入p.join()。

 

 

关于进程阻塞

    这种做法会让执行时间短的进程先执行完成。有时候有很多进程需要执行,但是我们希望等某一个进程执行完毕之后,再执行其它进程,这个时候该怎么办呢?

    这个时候就用到join了,这样会让使用join的进程先执行完成之后,再执行其它进程。

if __name__ == '__main__':
    p1 = Process(target=eat,args=("楼下小黑",))
    p2 = Process(target=watchTV,args=("楼下小黑",))
    p1.start()
    p1.join()
    p2.start()

结果为:

楼下小黑在吃饭
楼下小黑在看电视 

 

 

在子进程中修改全局变量

    在子进程中修改全局变量,对父进程中的全局变量没有影响,原因是,进程是程序执行和资源分配的基本单位,每个进程都有自己的数据、代码和堆栈。子进程当中已经备份了一份独立的资源,在执行子进程的过程当中修改的资源都只是子进程自己的那一份,所以不会对父进程的的数据产生影响。也就是父进程的n和子进程的n是两个不同的变量。

from multiprocessing import Process

n = 10
def test():
    print("子进程开始执行")
    global n
    n = n+1
    print("n在子进程中的值为{}".format(n))
    print("子进程结束执行")
if __name__ == '__main__':
    print("父进程开始执行")
    p = Process(target=test)
    p.start()
    p.join()
    print("n在父进程中为{}".format(n))
    print("父进程结束")
"""
父进程开始执行
子进程开始执行
n在子进程中的值为11
子进程结束执行
n在父进程中为10
父进程结束
"""

 

 

进程的另一种创建方式

进程的另一种创建方式,就是对Process类进行继承,然后写出自己的进程类,当调用start()方法时,程序会自动执行run()函数。

from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self):
        Process.__init__(self)
    def run(self):
        print("开始运行程序")
        time.sleep(2)
        print("程序结束")

if __name__ == '__main__':
    p = MyProcess()
    p.start() 

 

 

 

进程池的概念

    进程池可以放很多进程,当我们需要执行100个进程的时候,创建100个进程显然不合适,因为对系统的开销太大了。这个时候怎么办呢?这就用到进程池了,我们可以设置一个进程池,进程中只能同时运行几个进程,当进程池中有程序执行完成之后,再让未执行过的进程在进程池中执行,这样一来只需要几个进程,就能将原先需要开100个进程才能执行完的进程执行完。

代码如下:

from multiprocessing import Pool
import time

def build(n):
    print("第{}跑道开始修建".format(n))
    time.sleep(0.2)
    return n
def endBuild(n):
    print("~~~~~第{}跑道修建完成".format(n))
if __name__ == '__main__':
    p = Pool(3)
    for i in range(100):
        p.apply_async(func=build,args=(i,),callback=endBuild)
    p.close()
    p.join()

上面的build需要有返回值,它的返回值作为endBuild的位置参数传进去,当build函数执行完成后,就会执行endBuild方法。

 

 

进程通信队列

队列是一种先进先出的数据结构,进程之间有时候需要共享一些信息,这个时候就用到了队列。队列需要由父进程创建,供子进程来使用,现在有两个方法makeWord和readWords,以及一个队列q,方法获得了队列的引用,两个方法对一个队列的引用进行更改就实现了队列间的通信。

from multiprocessing import Queue,Process
import os,time

def makeWords(q):
    print("启动写子进程%s"%(os.getpid()))
    for chr in ["A","B","C","D"]:
        q.put(chr)
        time.sleep(1)
    print("结束写子进程%s"%(os.getpid()))

def readWords(q):
    print("启动读子进程%s"%(os.getpid()))
    while True:
        value = q.get(True)
        print("value="+ value)

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=makeWords,args=(q,))
    pr = Process(target=readWords,args=(q,))
    pw.start()
    pr.start()

    pw.join()
    pr.terminate() #只要pw结束就强行结束pr

    print("父进程结束")
"""
启动写子进程23676
启动读子进程17904
value=A
value=B
value=C
value=D
结束写子进程23676
父进程结束
"""

声明:以上内容作为本人理解,希望整理出一份全面的文章供大家分享,可能有不完善的地方,欢迎前辈指正。