进程

进程

计算机中已运行程序,如打开的QQ,打开的有道词典等等都是进程,在windows中可使用任务管理器查看运行的进程。

特点
1.进程是相互独立的,默认是不能共享数据,可通过队列或管道共享数据。
2.进程的崩溃不会影响到其他进程。

创建进程

可使用4种方式创建进程。

  1. os.fork()函数:不支持Windows系统,略过。
  2. multiprocessing模块Process类
  3. Process子类
  4. Pool进程池

使用multiprocessing模块创建进程

multiprocessing模块提供Process类来创建进程对象。

语法

Process([group [,name [,args [,kwargs]]]])
'''
group:参数未被使用,其值始终为None
target:当前进程启动时可调用的对象,可以为函数
name:表示当前进程对象的别名,默认值为Process-n,n从1开始递增,以此表示第几个进程
args:表示传递给target设置的函数的参数,为元组
kwargs:表示传递给target设置的函数的参数,为字典
'''

案例

from multiprocessing import Process
import time

def test(intervel):
    time.sleep(intervel)
    print('我是子进程')

def main():
    print('父进程开始执行')
    #创建一个子进程,函数为test(),args参数为元组,其值为2
    p = Process(target=test,args=(2,))
    #使用p对象的start()方法开始执行进程
    p.start()
    print('父进程已结束')

if __name__ == '__main__':
    main()
'''
out:
父进程开始执行
父进程已结束
我是子进程
'''
#这里父进程没有等待子进程,如果父进程要等待子进程结束后结束,可以使用join(),后续会讲到。

Process类常用方法

方法 说明
is_alive() 判断进程是否还在执行,返回布尔值
join([timeout]) 是否等待进程实例结束,参数为秒
start() 启动进程
run() 没有指定target参数,启动进程实例,就会执行对象种的run()方法
terminate() 立刻终止进程

Process类常用属性
name:当前进程实例别名,默认为Process-n,n从1开始递增,依次表示第几个进程
pid:当前进程实例的pid值

案例

from multiprocessing import Process
import time
import os

def child_1(intervel):
    print('子进程1(%s)开始执行,它的父进程是%s'%(os.getpid(),os.getppid()))
    #计算运行时间
    t_start = time.time()
    time.sleep(intervel)
    t_end = time.time()
    print('子进程1(%s) 执行时间为%0.2f秒' %(os.getpid(),t_end-t_start))

def child_2(intervel):
    print('子进程2(%s)开始执行,他的父进程是%s' %(os.getpid(),os.getppid()))
    t_start = time.time()
    time.sleep(intervel)
    t_end = time.time()
    print('子进程2(%s) 执行时间为%0.2f秒' %(os.getpid(),t_end-t_start))

def main():
    print('父进程开始执行,他的PID为:%s' %os.getpid())
    #创建一个子进程,子进程执行的函数为child_1,传递一个参数2
    p1 = Process(target=child_1,args=(1,))
    #创建第二个子进程,子进程执行的函数为child_2,传递两个参数
    p2 = Process(target=child_2,name='mrsoft',args=(2,))
    p1.start()
    p2.start()
    #查看子进程的是否还在执行
    print('p1.is_alive=%s' % p1.is_alive())
    print('p2.is_alive=%s' % p2.is_alive())
    #查看子进程的实例别名
    print('p1.name=%s' %p1.name)
    print('p2.name=%s' %p2.name)
    #查看子进程的pid
    print('p1.id=%s' %p1.pid)
    print('p2.id=%s' %p2.pid)
    #父进程等待子进程结束
    p1.join()
    p2.join()
    print('父进程已结束')

if __name__ == '__main__':
    main()
'''
out:
主进程开始执行,他的PID为:23300
p1.is_alive=True
p2.is_alive=True
p1.name=Process-1 #没有设置name属性,系统默认赋值
p2.name=mrsoft
p1.id=24004
p2.id=7452
子进程2(7452)开始执行,他的父进程是23300
子进程1(24004)开始执行,它的父进程是23300
子进程1(24004) 执行时间为1.00秒
子进程2(7452) 执行时间为2.00秒
进程已结束
'''

父子进程之间的关系
父子进程相互独立,单独结束父进程,子进程会变成独立的进程。
参考资料

使用Process子类创建子进程

使用第一种方式创建进程,每创建一个进程都需要实例化一个Process类对象,并定义一个进程执行函数,很不方便。
使用Process子类继承Process类,将进程执行函数写在子类中,可重复使用。

案例

#使用Process子类创建2个子进程
from multiprocessing import Process
import time
import os

#自定义子进程类,继承Process
class SubProcess(Process):
    #name需要设置一个默认值,因为p2没有传递name参数
    def __init__(self,interval,name=''):
        #调用父类的初始化方法
        super(SubProcess,self).__init__() #此行代码等同于 Process.__init__(self)
        self.interval = interval
        if name:
            self.name = name

    def run(self):
        print('子进程(%s)开始执行,它的父进程是%s' % (os.getpid(), os.getppid()))
        # 计算运行时间
        t_start = time.time()
        time.sleep(self.interval)
        t_end = time.time()
        print('子进程(%s) 执行时间为%0.2f秒' % (os.getpid(), t_end - t_start))

def main():
    print('父进程开始执行,他的PID为:%s' %os.getpid())
    #自定义SubProcess类创建子进程
    p1 = SubProcess(interval=1,name='mrsoft')
    p2 = SubProcess(interval=2)
    p1.start() #自动调用SubProcess类中的run()方法
    p2.start()
    #查看子进程的是否还在执行
    print('p1.is_alive=%s' % p1.is_alive())
    print('p2.is_alive=%s' % p2.is_alive())
    #查看子进程的实例别名
    print('p1.name=%s' %p1.name)
    print('p2.name=%s' %p2.name)
    #查看子进程的pid
    print('p1.id=%s' %p1.pid)
    print('p2.id=%s' %p2.pid)
    #父进程等待子进程结束
    p1.join()
    p2.join()
    print('父进程已结束')

if __name__ == '__main__':
    main()
'''
out:
主进程开始执行,他的PID为:26264
p1.is_alive=True
p2.is_alive=True
p1.name=mrsoft
p2.name=SubProcess-2
p1.id=276
p2.id=12264
子进程(276)开始执行,它的父进程是26264
子进程(12264)开始执行,它的父进程是26264
子进程(276) 执行时间为1.00秒
子进程(12264) 执行时间为2.00秒
进程已结束
'''
#注意:因为没有指定target参数,start()方法会默认调用类中的run()方法

使用Pool进程池创建进程

可以很方便的创建多个进程。
通过Multiprocessing模块提供的Pool类实现。

非堵塞和堵塞
堵塞:必须等待上一个进程推车才能执行下一个进程,相当于你做完了,我再做。
非堵塞:进程并行执行,相当于我们一起做。

Pool类的常用方法

方法 说明
apply_async(func [,args[,kwds]]) 使用非阻塞调用func()函数,args为传递给func()的参数列表,kwds为关键字参数
apply(func [,args[,kwds]]) 使用阻塞方式调用func函数
close() 关闭进程池
terminate() 立刻终止进程
join() 阻塞父进程让其等待子进程退出,必须在close()或terminate()之后使用

案例

from multiprocessing import Pool
import os
import time

def task(name):
    print('子进程(%s) 执行任务%s' %(os.getpid(),name))
    time.sleep(1) #防止运行过快,其余两个子进程抢不到任务

if __name__ == '__main__':
    print('父进程(%s)' %os.getpid())
    p = Pool(3) #创建3个子进程
    for i in range(10): #循环10次,执行10次任务
        p.apply_async(task,args=(i,)) #使用非阻塞方式,执行函数task
    p.close() #关闭进程池
    p.join() #等待子进程结束,join方法必须在close或terminate之后使用
    print('所有子进程已结束')
'''
out:
父进程(26340)
子进程(25448) 执行任务0
子进程(21672) 执行任务1
子进程(20020) 执行任务2
子进程(25448) 执行任务3
子进程(21672) 执行任务4
子进程(20020) 执行任务5
子进程(25448) 执行任务6
子进程(21672) 执行任务7
子进程(20020) 执行任务8
子进程(25448) 执行任务9
所有子进程已结束
'''

进程间的通信

每个子进程有自己的空间,两者之间是不能通信的。
python中可使用队列(Queue)和管道(Pipe)来让进程之间相互通信。

案例

#测试进程与进程之间是否能通信
from multiprocessing import Process

#创建一个全局变量,让其在进程中传递
g_num = 100

def plus():
    print('子进程1开始')
    global g_num
    g_num += 1
    print('g_num is %d' %g_num)
    print('子进程1结束')

def minus():
    print('子进程2开始')
    global  g_num
    g_num -=1
    print('g_num is %d' %g_num)
    print('子进程2结束')

if __name__ == '__main__':
    print('父进程开始')
    p1 = Process(target=plus)
    p2 = Process(target=minus)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('父进程结束')
'''
out:
主进程开始
子进程1开始
g_num is 101
子进程1结束
子进程2开始
g_num is 99
子进程2结束
主进程结束
'''

队列的使用

可使用multiprocessing模块的Queue类创建队列。
初始化q=Queue(num)对象时,如果num为空或者负数则表示接收的消息没有数量限制。

方法 说明
q.qsize() 返回队列中包含消息的数量
q.empty() 判断队列是否为空,返回布尔值
q.full() 判断队列是否满员,返回布尔值
q.get([block[,timeout]]) 从队列中取出一条消息,block默认为true;
1. 当block为Ture,且未设置timeout,此时队列若为空,程序会一直等待直到读到消息为止;
2. 如果设置了timeout,则会等待timeout秒,若还没读到消息,则会抛出Queue.Empty异常;
3. 如果block为Flase,消息队列为空,则立刻抛出Queue.Empty异常
q.get_nowait() 相当于q.get(False)
q.put(item,[block[,timeout]]) 将item消息写入队列,block默认为true;
1. 当block为Ture,且未设置timeout,消息队列如果没有空间写入,程序会一直等待直到队列腾出空间为止;
2. 如果设置了timeout,则会等待timeout秒,若还没空间,则抛出Queue.Full异常;
3. 如果block为False,消息队列没有空间写入则会立刻抛出异常
q.put_nowait(item) 相当于q.put(item,False)

案例

from multiprocessing import Queue

if __name__ =='__main__':
    q = Queue(3) #最多可接收3条数据
    q.put('数据1')
    q.put('数据2')
    q.put('数据3')
    print('队列是否满员:%s' %q.full())

    try:
        q.put('数据4',True,2) #timeout为2,等待2秒还是不能将数据放入队列就抛出异常
    except:
        print('队列已满,现有数据数量%s' %q.qsize())

    if not q.empty():
        print('从队列取数据')
        for i in range(q.qsize()):
            print(q.get_nowait())
'''
out:
队列是否满员:True
队列已满,现有数据数量3
从队列取数据
数据1
数据2
数据3
#先入先出,所以先输出数据1
'''

使用队列在进程间通信

直接上案例,走你!

案例

from multiprocessing import Process,Queue
import time

def write_task(q):
    if not q.full(): #如果队列没满
        #使用循环向队列中写入五条数据
        for i in range(5):
            data = '数据'+str(i)
            q.put(data)
            print('写入%s' %data)

def read_task(q):
    time.sleep(1) #等待一秒
    while not q.empty():
        print('读取%s' %q.get(True,2))

if __name__ == '__main__':
    print('父进程开始')
    q = Queue() #创建一个队列,不定长
    #创建写入进程,执行函数write_task,参数为队列对象q
    pw = Process(target=write_task,args=(q,))
    #创建读取进程
    pr = Process(target=read_task,args=(q,))
    pw.start()
    pr.start()
    pw.join() #让主进程等待子进程执行结束
    pr.join()
    print('父进程结束')

'''
out:
主进程开始
写入数据0
写入数据1
写入数据2
写入数据3
写入数据4
读取数据0
读取数据1
读取数据2
读取数据3
读取数据4
主进程结束
'''

学习来自:《python从入门到项目实践》明日科技 第十六章(视频)
posted @ 2020-12-02 15:46  努力吧阿团  阅读(82)  评论(0编辑  收藏  举报