并发编程 之 进程

进程

进程:正在进行的一个过程或者说一个任务。而负责执行任务则是cpu。
程序仅仅只是一堆代码而已,而进程指的是程序的运行过程。

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

开启进程的两种方式

创建进程的第一种方式
from multiprocessing import Process
import time

def task(name):
    print('%s is running'%name)
    time.sleep(1)
    print('%s is over'%name)

if __name__ == '__main__':
    p1 = Process(target = task,args = ('subprocess',)) # 实例化了一个Process对象
    p1.start() # 告诉操作系统创建一个进程
    print('main process is running')
    
输出:
main process is running
subprocess is running
subprocess is over
创建进程的第二种方式
from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):  # 必须写run方法
        print('%s is running'%self.name)
        time.sleep(1)
        print('%s is over'%self.name)

if __name__ == '__main__':
    obj = MyProcess('subprocess')
    obj.start() # 告诉操作系统创建一个进程
    print('main process is running!')
    
输出:
main process is running!
subprocess is running
subprocess is over

注意:

在 windows 系统中,创建进程会将代码以模块的方式从头到位的加载一边,所以要把创建进程的代码写在if __ name __ == ‘__ main __’ : 代码块里面。

进程对象的join方法

阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程。

from multiprocessing import Process
import time

def task(name,n):
    print('%s is running'%name)
    time.sleep(n)
    print('%s is over'%name)

if __name__ == '__main__':
    start_time = time.time() # 通过时间验证join()的顺序不会影响子进程的运行
    p_list = [] # 将创建的进程添加到列表内
    for i in range(3):
        p = Process(target = task,args= ('shuprocess%s'%i,i))
        p.start() # 这句话只是告诉操作系统需要进程
        p_list.append(p)
    for i in p_list:
        i.join() # 主进程等待子进程结束 才继续运行
    print('main process is running!',time.time()-start_time)

输出:
shuprocess2 is running
shuprocess0 is running
shuprocess0 is over
shuprocess1 is running
shuprocess1 is over
shuprocess2 is over
main process is running! 3.2183451652526855

进程之间内存隔离

进程与进程之间数据是隔离的!

from multiprocessing import Process
x = 100
def task():
    global x
    x = 200

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print('main process', x) # 进程与进程之间数据是隔离的

输出:
main process 100

进程的相关方法

p.start():启动进程,并调用该子进程中的p.run() 。

p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 。

p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁。

p.is_alive():如果p仍然运行,返回True。

p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 。

p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置。

p.name:进程的名称。

p.pid:进程的pid。

p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束。

p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功。

from multiprocessing import Process,current_process
import time
import os

def task():
    print('%s is running'%current_process().pid)
    print('%s is running'%os.getpid())
    time .sleep(2)
    print('%s is over'%os.getppid())

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    time.sleep(2)
    p.terminate() # 杀死子进程
    time.sleep(1)
    print(p.is_alive()) # 判断子进程是否存活
    print('main process')

输出:
4496 is running
4496 is running
False
main process

守护进程

主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

from multiprocessing import Process
import time

def task(name):
    print('%s is living'%name)
    time.sleep(3)
    print('%s is Normal death'%name)

if __name__ == '__main__':
    p = Process(target=task,args=('subprocess',))
    p.daemon = True  # 必须在p.start开启进程命令之前声明
    p.start()
    print('main process is dying')
输出:
subprocess is living
main process is dying

互斥锁

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,

而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理

info.py
{"ticket": 1}
from multiprocessing import Process,Lock
import json
import time
import random

def search(i):
    with open('info','r',encoding='utf-8') as f:
        data = json.load(f)
    print('用户查询余票数:%s'%data.get('ticket'))


def buy(i):
    # 买票之前还得先查有没有票!
    with open('info','r',encoding='utf-8') as f:
        data = json.load(f)
    time.sleep(random.randint(1,3))  # 模拟网络延迟
    if data.get('ticket') >0:
        data['ticket'] -= 1  # 买票
        with open('info','w',encoding='utf-8') as f:
            json.dump(data,f)
        print('用户%s抢票成功'%i)
    else:
        print("用户%s查询余票为0"%i)
def run(i,mutex):
    search(i)
    mutex.acquire()  # 抢锁   一把锁不能同时被多个人使用,没有抢到的人,就一直等待锁释放
    buy(i)
    mutex.release()  # 释放锁

if __name__ == '__main__':
    mutex = Lock()
    for i in range(10):
        p = Process(target=run,args=(i,mutex))
        p.start()

僵尸进程与孤儿进程

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

参考博客:http://www.cnblogs.com/Anker/p/3271773.html ?

基于队列实现进程间通信

from multiprocessing import Queue,Process

def producer(q):
    q.put('hello baby!')

def consumer(q):
    print(q.get())

if __name__ == '__main__':
    q = Queue()  # 生成一个队列对象
    p1 = Process(target=producer,args=(q,))
    c1 = Process(target=consumer,args=(q,))
    p1.start()
    c1.start()

生产者消费者模型

生产者 : 代指生产数据的任务
消费者 : 代指处理数据的任务
该模型的工作方式:
生产生产数据传递消费者处理

 实现方式:
      生产者---->队列<------消费者

为何要用生产者消费者模型:
当程序中出现明细的两类任务,一类负责生产数据,一类负责处理数据
就可以引入生产者消费者模型来实现生产者与消费者的解耦合,平衡生产能力与消费能力,从提升效率

通过往队列内添加None来判断消费着是否将数据处理完毕
import time,random
from multiprocessing import Process,Queue

def producer(name,food,q):
    for i in range(3):
        res='%s%s' %(food,i)
        time.sleep(random.randint(1,3)) #模拟生产数据的时间
        q.put(res)
        print('[%s]生产了<%s>' %(name,res))

def consumer(name,q):
    while True:
        res=q.get()
        if res is None:break
        time.sleep(random.randint(1,3)) #模拟处理数据的时间
        print('吃货[%s]吃了<%s>' %(name,res))

if __name__ == '__main__':
    q=Queue()
    # 生产者们
    p1=Process(target=producer,args=('小厨师','大包子',q))
    p2=Process(target=producer,args=('中厨师','中包子',q))
    p3=Process(target=producer,args=('大厨师','小包子',q))
    # 消费者们
    c1=Process(target=consumer,args=('大吃货',q))
    c2=Process(target=consumer,args=('小吃货',q))
	# 开启进程
    p1.start()
    p2.start()
    p3.start()
    c1.start()
    c2.start()
	# 等待生产者数据生产完毕
    p1.join()
    p2.join()
    p3.join()
    q.put(None) # 往队列内添加None,结束一个消费者进程
    q.put(None) # 往队列内添加None,结束一个消费者进程
    print('主')

以上示例如果有很多消费者,必须要手动往队列内添加对应的None来结束进程。

import time,random
from multiprocessing import Process,JoinableQueue # 导入JoinableQueue,来判断队列内的数据是否取完

def producer(name,food,q):
    for i in range(3):
        res='%s%s' %(food,i)
        time.sleep(random.randint(1,3)) #模拟生产数据的时间
        q.put(res)
        print('厨师[%s]生产了<%s>' %(name,res))

def consumer(name,q):
    while True:
        res=q.get()
        time.sleep(random.randint(1,3)) #模拟处理数据的时间
        print('吃货[%s]吃了<%s>' %(name,res))
        q.task_done() # 告诉你的队列,你已经将数据取出并且处理完毕

if __name__ == '__main__':
    q=JoinableQueue()
    # 生产者们
    p1=Process(target=producer,args=('小厨师','大包子',q))
    p2=Process(target=producer,args=('中厨师','中包子',q))
    p3=Process(target=producer,args=('大厨师','小包子',q))
    # 消费者们
    c1=Process(target=consumer,args=('大吃货',q))
    c2=Process(target=consumer,args=('小吃货',q))
    c1.daemon=True # 将消费者c1设置为守护进程
    c2.daemon=True # 将消费者c2设置为守护进程

    p1.start()
    p2.start()
    p3.start()
    c1.start()
    c2.start()

    p1.join()
    p2.join()
    p3.join()
    q.join() # 主进程等q结束,即q内数据被取干净了,确保两个消费者数据处理完毕
    print('主') # 主进程结束,两个守护进程也随着主进程的结束而结束
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值