并发编程-进程

一、什么是进程

  程序:一行行代码组成         

  进程:正在运行的程序

    进程即正在执行的一个过程。进程是对正在运行程序的一个抽象

  狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
  广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。

 

二、多道技术

  1.空间上的复用
    多个程序共用一套计算机硬件

  2.时间上的复用
    切换+保存状态
      1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)
      2.当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)

  并发:看起来像同时运行的就可以
  并行:真正意义上的同时执行
     单核的计算机能不能实现并行,但是可以实现并发

  

三、同步异步、阻塞非阻塞

  1.进程调度:

    时间片轮转法+多级反馈队列

 

  2.进程三状态图

  

  同步异步:表示的是任务的提交方式

  阻塞非阻塞:表示程序的运行状态

  ps:同步异步 阻塞非阻塞是两对概念 不能混为一谈

 

四、进程在python中的运用 

  1.创建进程的两种方式

   使用Process实例化

   继承Process类重写run方法

from multiprocessing import Process
import time

def test(name):
    print('%s is running'%name)
    time.sleep(2)
    print('%s is over'%name)

if __name__ == '__main__':  # window创建进程一定要在if __name__ == '__main__':代码块内创建,否则会报错
    p = Process(target=test,args=('Tom',))  # 创建一个进程对象
    p.start()  # 告诉操作系统帮你创建一个进程
    print('')
直接调用模块
# 创建进程的第二张方式
from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,name):
        super().__init__()
        self.name = name
    def run(self):
        print('%s is running'% self.name)
        time.sleep(2)
        print('%s is over' % self.name)

if __name__ == '__main__':
    p = MyProcess('tom')
    p.start()
    print('')
通过继承Process类
   特点:
   1.在windos操作系统中,创建进程会将代码以模块的方式 从上往下执行一遍
    linux会直接将代码完完整整的拷贝一份
   2.创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去
    一个进程对应在内存就是一块独立的内存空间
   3.进程与进程之间数据时隔离的,无法直接交互
   但是可以通过某些技术实现间接交互

  2.进程join方法:

    通过join方法实现主进程等待子进程运行结束后,自己才继续运行

from multiprocessing import Process
import time

def test(name,i):
    print('%s is running'%name)
    time.sleep(i)
    print('%s is over'%name)
if __name__ == '__main__':
    # p_list = []
    # for i in range(3):
    #     p = Process(target=test,args=('进程%s'%i,i))
    #     p.start()
    #     p_list.append(p)
    # for p in p_list:
    #     p.join()
    p = Process(target=test,args=('egon',1))
    p1 = Process(target=test,args=('kevin',2))
    p2 = Process(target=test,args=('jason',3))
    start_time = time.time()
    p.start()  # 仅仅是告诉操作系统帮你创建一个进程 至于这个进程什么时候创  操作系统随机决定
    p1.start()
    p2.start()
    p2.join()
    p.join()
    p1.join()

    # 主进程代码等待子进程运行结束 才继续运行
    # p.join()  # 主进程代码等待子进程运行结束
    print('')
    print(time.time() - start_time)
View Code

 

  3.进程间对象及其他方法

    p.terminate()  # 杀死进程,告诉操作系统杀死一个进程

    p.is_alive()  # 判断进程是否存活

    os.getpid()  和  current_process().pid  一样  # 获取进程ID

    os.getppid()  # 获取父进程ID

from multiprocessing import Process,current_process
import os
import time


def test(name):
    # print('%s is running'%name,current_process().pid)
    print('%s is running'%name,'子进程%s'%os.getpid(),'父进程%s'%os.getppid())
    time.sleep(3)
    print('%s is over'%name)


if __name__ == '__main__':
    p = Process(target=test,args=('egon',))
    p.start()
    p.terminate()  # 杀死当前进程  其实是告诉操作系统帮你杀死一个进程
    time.sleep(0.1)
    print(p.is_alive())  # 判断进程是否存活
    # print('主',current_process().pid)
    print('',os.getpid(),'主主进程:%s'%os.getppid())
View Code

  4.进程间数据互相隔离

from multiprocessing import Process
import time


money = 100

def test():
    global money
    money = 99999999


if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    p.join()
    print(money)
View Code

  5.守护进程

  会随着主进程的结束而结束。

   daemon 这一句必须在start之前使用

  主进程创建守护进程

    其一:守护进程会在主进程代码执行结束后就终止

    其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

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

  p.daemon = True # 将该进程设置为守护进程 这一句话必须放在start语句之前 否则报错

from multiprocessing import Process
import time


def test(name):
    print('%s总管正常活着'%name)
    time.sleep(3)
    print('%s总管正常死亡'%name)


if __name__ == '__main__':
    p = Process(target=test,args=('egon',))
    p.daemon = True  # 将该进程设置为守护进程   这一句话必须放在start语句之前 否则报错
    p.start()
    time.sleep(0.1)
    print('皇帝jason寿正终寝')
View Code

  6.互斥锁 ****

  mutex = Lock() # 生成一把锁

  mutex.acquire()  # 抢锁

  mutex.release()  # 释放锁

  当多个进程操作同一份数据的时候 会造成数据的错乱,这个时候必须加锁处理,

    将并发变成串行

    虽然降低了效率但是提高了数据的安全

  注意:

  1.锁不要轻易使用,容易造成死锁现象

  2.只在处理数据的部分加锁,不要在全局加锁

  (锁必须在主进程中产生,产生子进程去使用)

from multiprocessing import Process,Lock
import time
import json

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

# 买票
def buy(i):
    with open('data','r',encoding='utf-8') as f:
        data = f.read()
    t_d = json.loads(data)
    time.sleep(1)
    if t_d.get('ticket') > 0:
        # 票数减一
        t_d['ticket'] -= 1
        # 更新票数
        with open('data','w',encoding='utf-8') as f:
            json.dump(t_d,f)
        print('用户%s抢票成功'%i)
    else:
        print('没票了')


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()
互斥锁案例

  7.僵尸进程与孤儿进程

  父进程回收子进程资源的两种方式

    1.join方法

    2.父进程正常死亡

  所有的进程都会步入僵尸进程

  孤儿进程

    子进程没死 父进程意外死亡

    针对linux会有儿童福利院(init) 如果父进程意外死亡他所创建的子进程都会被福利院收养

 五、进程间的通信

  1.实现多进程间数据传递,通过Queue方法,Queue是多进程安全的队列

from multiprocessing import Queue

q = Queue(5)  # 括号内可以传参数,表示的是这个队列的最大存储数

# q.put() 往队列中添加数据
q.put(1)
# q.full()  # 判断队列是否满了
q.put(2)
q.put(3)
q.put(4)
q.put(5)
# print(q.full())  # 返回True,False
# q.put(6)  # 当队列满了之后,子放入数据,不会报错,会原地等待,直到队列中有数据被取走(阻塞态)

q.get()  # 取数据,当队列中数据被取完之后,再次获取,程序会阻塞,直到有人往队列中放入值。
q.empty() # 判断队列中的数据是否取完
q.get_nowait() # 取值,没有值不等待直接报错

'''
full
get_nowait
empty
都不适用于多进程的情况
'''
队列

  2.通信ipc机制

from  multiprocessing import Process,Queue

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

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

if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer,args=(q,))
    c = Process(target=consumer,args=(q,))
    p.start()
    c.start()

'''
子进程放数据,主进程获取数据
两个子进程互相放取数据
'''
IPC机制

  3.生产者消费模型  

  如何做到消费者消费完数据之后代码立刻结束
  1.利用join等待生产者生产完数据 再往队列中添加特定信息(None)
   有几个消费者就必须有几个None

  2.JoinableQueue 能够被join的q
   q.task_done() 告诉队列数据被取出
   q.join() 等待队列数据完全被取完
   将所有的消费者设置为守护进程

from multiprocessing import Process, Queue, JoinableQueue
import time
import random

def producer(name,food,q):
    for i in range(10):
        data  = '%s做了%s%s'%(name,food,i)
        time.sleep(random.random())
        q.put(data)
        print(data)

def consumer(name,q):
    while True:
        data = q.get()
        if data == None:break
        print('%s吃了%s'%(name,data))
        time.sleep(random.random())
        q.task_done()  # 告诉队列已经从中取出了一个数据,并且处理完毕了


if __name__ == '__main__':
    q = JoinableQueue()  # 能够被等待的数列,与q.task_done连用,表示已经处理好了,拿出了数据

    p = Process(target=producer, args=('tom','馒头',q))
    p1 = Process(target=producer, args=('jason', '馒头', q))
    c = Process(target=consumer, args=('jack',q))
    c1 = Process(target=consumer, args=('jerry', q))
    p.start()
    p1.start()
    c.daemon = True
    c1.daemon = True  # 守护,子进程随主进程结束而关闭,
    c.start()
    c1.start()
    p.join()  # 等待子进程结束自己再运行
    p1.join()

    q.join()  # 等队列中数据全部取出
'''
JoinableQueue内部有自己机制,检测数据确确实实被拿完
q.task_done
q.join
'''
    # q.put(None)  # 可以实现,太low
    # q.put(None)
生产消费者模型

 

posted @ 2019-08-09 19:44  小王八+1  阅读(176)  评论(0编辑  收藏  举报