进程相关

内容概要

  一、僵尸进程、孤儿进程、守护进程、进程其他方法

  二、互斥锁

  三、队列

  四、生产者消费者模型

 

1、僵尸进程、孤儿进程

  僵尸进程,就是进程结束了,但是资源未被回收。这种现象出现在所有进程中,因为进程虽然要结束,但也要保留一些信息提供给父进程来访问(比如执行时间)

  孤儿进程,子进程执行过程中父进程突然死亡,子进程无法被回收。

    -操作系统有机制会处理这类孤儿进程

 

  守护进程,守护进程的出现避免了孤儿进程的产生

    -设置守护进程:只要在申请开启进程前设置

      -进程对象.daemon = True即可

from multiprocessing import Process
import time

def task():
    print('妃子还活着')
    time.sleep(1)
    print('妃子正常死亡')

if __name__ == '__main__':
    p = Process(target=task,)
    p.daemon = 1
    p.start()
    print('皇帝驾崩了,手下理应都死')

 

  注意:一般主进程会在子进程都执行接受后,才会回收资源。而守护进程是在主进程执行代码结束后立即死亡。

 

  进程相关(与运维相关)

    -类似端口,每一个进程在申请时都有一个独有的id号,即PID。

      可以通过两种方式获取进程PID号

        -方式一:利用current_process().pid获取当前进程的pid号

        -方式二:利用os模块的os.getpid方法,还可以使用os.getppid方法获取父进程的PID号(推荐)

from multiprocessing import Process, current_process
import os


def task():
    print(current_process().pid)  # 获取子进程pid号
    print(os.getppid())


if __name__ == '__main__':
    p = Process(target=task, )
    p.start()
    print(os.getpid())  # 获取当前进程的pid号
    print(os.getppid())  # 获取当前进程的父进程,即pycharm的pid号

    可以在命令行或者终端中查看和筛选进程号

      -windows系统下

        -在命令行输入tasklist查看,tasklist |findstr PID号查看进程详情

      -mac系统下

        -在终端中输入ps aux查看,ps aux|grep PID号查看进程详情

2、互斥锁

  多进程能提高cpu使用效率,但是在程序运行过程中也会带来一些问题,其中数据混乱会是很大的问题

  互斥锁可以解决这个问题,它通过牺牲程序运行效率,保证了数据的安全,互斥锁要尽可能少用,而且理应在执行数据读写入时才使用

  数据混乱实例

    创建一个json文件,其中存储了{"ticket_num":1}的数据

    模拟抢票程序

from multiprocessing import Process
import json
import time
import random


def search(i):
    with open('data',mode='r',encoding='utf-') as f:
        dic = json.load(f)
    print('用户{},剩余票数:{}'.format(i,dic.get('ticket_num')))

def buy(i):
    # 服务端加载数据
    with open('data', mode='r', encoding='utf-8') as f:
        dic = json.load(f)
    ticket_num = dic.get('ticket_num')
    # 模拟网络延迟
    time.sleep(random.randrange(1,3))
    # 数据发送到客户端
    if ticket_num > 0:
        ticket_num -= 1
        dic['ticket_num'] = ticket_num
        with open('data', mode='w', encoding='utf-8') as f:
            json.dump(dic,f)
        print('用户{}抢票成功'.format(i))
    else:
        print('用户{}抢票失败'.format(i))

def run(i):
    search(i)
    buy(i)

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

    利用互斥锁解决数据错乱问题

from multiprocessing import Process,Lock
import json
import time
import random


def search(i):
    with open('data',mode='r',encoding='utf-') as f:
        dic = json.load(f)
    print('用户{},剩余票数:{}'.format(i,dic.get('ticket_num')))

def buy(i,lock):
    lock.acquire() # 上锁
    with open('data', mode='r', encoding='utf-8') as f:
        dic = json.load(f)
    ticket_num = dic.get('ticket_num')
    time.sleep(random.randrange(1,3))
    if ticket_num > 0:
        ticket_num -= 1
        dic['ticket_num'] = ticket_num
        with open('data', mode='w', encoding='utf-8') as f:
            json.dump(dic,f)
        print('用户{}抢票成功'.format(i))
    else:
        print('用户{}抢票失败'.format(i))
    lock.release()  # 解锁

def run(i,lock):
    search(i)
    buy(i,lock)

if __name__ == '__main__':
    lock = Lock() # 创建一把锁
    for i in range(6):
        p = Process(target=run,args=(i,lock,))
        p.start()

 

  利用Lock类创建一把锁对象后,将锁传给进程要执行的函数中

  lock.acquire方法和lock.release方法之间的代码同时有且只有一个进程可以执行,这期间其它进程会阻塞在lock.acquire这行代码中

 

3、队列

  队列是存储数据的方式、除此之外还有管道和堆栈

    -管道 subprocess模块 stdout、stderr管道

    -队列中的数据是 先进先出 它是管道+锁

    -堆栈中的数据是 后进先出

 

    进程之间数据是彼此隔离的,但是可以通过队列实现数据的交换

      from multprocessing import Queue模块使用队列

      q = Queue() 实例化对象,可以传参,参数设置队列中能存放的数据的最大值

      q.put()往队列中存放数据。当队列中数据满了,再放数据,会发生程序阻塞

      q.get()往队列中拿取数据。当队列中数据为空,再拿数据,会发生程序阻塞

      q.get(timeout=3)往队列中拿取数据。当队列中数据为空,等待timeout设定得事件后,会报错

      q.get_nowait()往队列中拿取数据。当队列中数据为空,再拿数据,会报错

      q.full()判断队列是否已满

      q.empty()判断队列是否为空

      注意:q.empty()、q.full()、q.get_nowait()在多进程中并不准确,例如使用q.empty时,也许前脚检测结果为空,后脚就有其他进程往队列中添加了数据

 

      Queue更多只会运用在测试中,实际生产中,会用到功能更强大的消息队列,比如: 

        -redis

        -kafka

        -RQ

 

4、生产者消费者模型

  什么是生产者消费者模型呢?

    -生产者指的就是生产数据的进程

    -消费者指的是将生产者生产的数据拿到并进行处理的进程

    -生产者和消费者之间应该加入一个桥梁,它就是队列

 

    ps:为了提高数据生产和处理的速度,不应该将生产数据和处理数据的代码写道统一文件中,只用主线程执行代码,按逻辑处理,那样会拖慢效率

 

    最理想的生产者和消费者模型应该是生产者生产了一个数据,消费者会马上处理这个数据;而且生产者生产了多少数据,消费者就消费多少数据

 

    模型示范1:

from multiprocessing import Queue,Process
import time
import random

def producer(q,i):
    time.sleep(random.randrange(2))
    data = '{}生产了蛋糕'.format(i)
    print(data)
    q.put(data)

def consumer(q):
    while 1:
        time.sleep(random.randrange(2))
        data = q.get()
        if data is None:
            break
        print('消费了{}'.format(data))


if __name__ == '__main__':
    l = []
    # 创建队列
    q = Queue()
    # 开启生产者进程
    for i in range(10):
        p = Process(target=producer,args=(q,i,))
        p.start()
        l.append(p)

    # 开启消费者进程
    for i in range(2):
        p = Process(target=consumer,args=(q,))
        p.start()

    # 保证生产者生产完数据
    for x in l:
        x.join()

    # 往队列中添加用于结束一个消费者进程的标识,这里用到的标识是None,有多少消费者就得放入多少结束标识符
    q.put(None)
    q.put(None)

    模型实例2:

from multiprocessing import Process,JoinableQueue
import time
import random

def producer(q,i):
    time.sleep(random.randrange(2))
    data = '{}生产了蛋糕'.format(i)
    print(data)
    q.put(data) # 每当执行一次put代码,q中的数据计数器+1

def consumer(q):
    while 1:
        time.sleep(random.randrange(2))
        data = q.get()
        if data is None:
            break
        print('消费了{}'.format(data))
        q.task_done() # 每次手动执行task_done命令,q中的数据计数器-1


if __name__ == '__main__':
    l = []
    # 创建队列
    q = JoinableQueue()
    # 开启生产者进程
    for i in range(10):
        p = Process(target=producer,args=(q,i,))
        p.start()
        l.append(p)

    # 开启消费者进程
    for i in range(2):
        p = Process(target=consumer,args=(q,))
        # 将所有消费者进程设置为守护进程
        p.daemon = 1
        p.start()

    # 保证生产者生产完数据
    for x in l:
        x.join()

    # 使用q.join()方法,当q中的数据计数为0时,才会放行代码
    q.join()
    # 由于主进程执行完毕,所有的守护进程(即消费者进程都会结束)

     

    生产者消费模型在实际生活中的例子:最典型的就是python的爬虫,从网页抓取数据是生产者,将得到的数据进行快速处理的是消费者

 

    ps: IPC机制(不清楚是啥)

from multiprocessing import Process,Queue
import time
# 研究思路 # 1、主进程跟子进程借助于队列通信 # 2、子进程跟子进程借助于队列通信 def producer(q):
   time.sleep(0.1) q.put(
'我是23号技师,很高兴为您服务') print('hello big baby') def consumer(q): print(q.get()) if __name__ == '__main__': q = Queue(5) p1 = Process(target=producer,args=(q,)) p2 = Process(target=consumer,args=(q,)) p2.start() p1.start() # print(q.get())

 

posted @ 2021-02-21 19:16  口乞厂几  阅读(52)  评论(0)    收藏  举报