day30 进程 同步 异步 阻塞 非阻塞 并发 并行 创建进程 守护进程 僵尸进程与孤儿进程 互斥锁

操作系统发展史

1.第一带计算机   真空管和穿孔卡片  没有进程 没有操作系统   

​    2.第二代计算机  7094 1401    晶体管  批处理系统 

​        输入输出  以及计算设备 不能互联  需要人参与    一批一批的处理   开发效率慢  并且串行执行

​    3.第三代计算机    集成电路 与多道技术 

​        多终端联机  spooling      同一台机器既能进行科学计算  又能做字符处理       通用计算机

​        多道技术   解决串行导致的效率低下问题

​        多用户终端    可以同时为多个用户提供服务  每个用户以为自己独享一台计算机   

​    4.第四代   个人电脑

​        大规模使用了集成电路,大多都提供了GUI界面  

多道技术

产生背景 ,所有程序串行 导致资源浪费

目的是让多个程序可以并发执行 , 同时处理多个任务

空间复用

指的是 同一时间 内存中加载多个不同程序数据,

每个进程间内存区域相互隔离,物理层面的隔离  

 时间复用       切换 + 保存

 切换条件:

1.一个进程执行过程中遇到了IO操作  切换到其他进程

2.运行时间过长,会被操作系统强行剥夺执行权力   

单纯的切换不够,必须在切换前保存当前的状态,以便于恢复执行

进程

一个正在被运行的程序就称之为进程,是程序具体执行过程,一种抽象概念

进程来自于操作系统

进程和程序的区别

程序就是一堆计算机可以识别文件,程序在没有被运行就是躺在硬盘上的一堆二进制

运行程序时,要从硬盘读取数据到内存中,CPU从内存读取指令并执行 ,

一旦运行就产生了进程

一个程序可以多次执行 产生多个进程,但是进程之间相互独立

当我们右键运行了一个py文件时 ,其实启动的是python解释器,你的py文件其实是当作参数传给了解释器

同步 异步 阻塞 非阻塞 并发 并行

同步 异步 指的是任务的提交方式
1.同步:任务提交之后 原地等待的任务的执行并拿到返回结果才走 期间不做任何事(程序层面的表现就是卡住了)

2.异步:任务提交之后 不再原地等待 而是继续执行下一行代码(结果是要的 但是是用过其他方式获取)
阻塞 非阻塞 指的是程序的运行状态
阻塞 : 程序遇到io操作是就进入了阻塞状态  ,程序无法继续执行其他代码

​	本地IO input      print     sleep    read  write       

​	网络IO recv  send

非阻塞: 程序正常运行中 没有任何IO操作   就处于非阻塞状态  ,或者通过某种方式使程序即时遇到了也不会停在原地,还可以执行其他操作,以提高CPU的占用率
并发 并行 说的是 任务的处理方式
并发: 多个任务看起来同时在处理 ,本质上是切换执行     速度非常快    

并行: 多个任务真正的同时执行    必须具备多核CPU  才可能并行  

进程的三种状态

  

 

就绪态,运行态,和阻塞态

多道技术会在进程执行时间过长或遇到IO时自动切换其他进程,意味着IO操作与进程被剥夺CPU执行权都会造成进程无法继续执行

创建进程

创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去,一个进程对应在内存就是一块独立的内存空间
进程与进程之间数据是隔离的 无法直接交互,但是可以通过某些技术实现间接交互
实例化Process,将要执行任务用target传入
from multiprocessing import Process
import time


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

# windows创建进程会将代码以模块的方式 从上往下执行一遍
# linux会直接将代码完完整整的拷贝一份
# windows创建进程一定要在if __name__ == '__main__':代码块内创建  否则报错

if __name__ == '__main__':
    # 创建一个进程对象
    p = Process(target=test,args=('lolo',))
    # 告诉操作系统帮你创建进程
    p.start()
    print('')
1
继承Process,并覆盖run方法, 将任务放入run方法中  
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('coco')
    p.start()
    print('')
2
1.在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程

2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管

进程间内存相互隔离

join函数

Process的对象具备一个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()
    p1 = Process(target=test,args=('dodo',2))
    p2 = Process(target=test,args=('lolo',1))
    p1.start()
    p2.start()
    p1.join()
View Code

进程间相互隔离

from multiprocessing import Process


money = 100

def test():
    global money
    money = 888

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

进程对象及其它方法

from multiprocessing import Process,current_process,active_children
import os
import time


def test(name):
    print('%s is running'%name,current_process().pid)
    print('%s run'%name,os.getpid(),os.getppid())
    time.sleep(1)
    print('%s is over'%name)

if __name__ == '__main__':
    p = Process(target=test,args=('lolo',))
    p.start()
    p.terminate()
    # 杀死当前进程  其实是告诉操作系统帮你杀死一个进程
    print(p.is_alive())
    # 判断进程是否存活
    print('',os.getpid(), os.getppid())
View Code
# p.join() # 等待子进程结束
# p.terminate()  # 终止进程
# print(p.name)  # 进程的名称
# print(p.is_alive()) #是否存活
# p.terminate() # 与start一样 都是给操作系统发送指令 所以会有延迟
# print(p.is_alive())
# print(p.pid)
# print(p.exitcode) # 获取退出码

  

守护进程

进程:一个正在运行的程序。

主进程创建守护进程:

1.守护进程会在主进程代码执行结束后就终止,

2.守护进程内无法再开启子进程,否则抛出异常。

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


def teat(name):
    print('%s 在'%name)
    time.sleep(1)
    print('%s 不在'%name)

if __name__ == '__main__':
    p = Process(target=teat,args=('coco',))
    p.daemon = True
    p.start()
    # p.daemon = True
    time.sleep(0.1)
    print('老板不在')
View Code

僵尸进程与孤儿进程

孤儿进程
孤儿进程指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程

孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。

僵尸进程
僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!

僵尸进程的危害:
 

由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。
在Linux中,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,
在python中,已经封装好了wait操作不需要我们自己清理。
但是系统所能使用的进程号是有限的,如果大量的产生[僵死进程],将因为没有可用的进程号而导致系统不能产生新的进程. 此为僵尸进程的危害,应当避免。

  

互斥锁

互相排斥的锁(如果这个资源已经被锁了,其他进程就无法使用了)
需要强调的是: 锁 并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行。
使用场景:  
  并发将带来资源的竞争问题,
当多个进程同时要操作同一个资源时,将会导致数据错乱的问题。

 

解决方案1:

​ 加join,

例子:
from multiprocessing import Process
import time


def task1():
    print('你好,阿森。。。')
    time.sleep(3)
    print('吃饭')
    time.sleep(3)
    print('下雨')


def task2():
    print('你好,阿三。。。')
    time.sleep(3)
    print('吃面')
    time.sleep(3)
    print('下雪')


def task3():
    print('你好,阿四。。。')
    time.sleep(3)
    print('吃米')
    time.sleep(3)
    print('下冰雹')


if __name__ == '__main__':
    p1 = Process(target=task1)
    p2 = Process(target=task2)
    p3 = Process(target=task3)

    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
​ 弊端: 1.把原本并发的任务变成了穿行,避免了数据错乱问题,但是效率降低了,这样就没必要开子进程了。

​        2.原本多个进程之间是公平竞争,join执行的顺序就定死了,这是不合理的。

解决方案2:

​ 就是给公共资源加 锁----互斥锁。

例子:

from multiprocessing import Process,Lock
import time,random
def task1(lock):
    # 上锁
    lock.acquire()      #就等同于一个if判断
    print('你好,阿森。。。')
    time.sleep(random.randint(0,3))
    print('吃饭')
    time.sleep(random.randint(0, 3))
    print('下雨')
    #解锁
    lock.release()


def task2(lock):
    lock.acquire()
    print('你好,阿三。。。')
    time.sleep(random.randint(0, 3))
    print('吃面')
    time.sleep(random.randint(0, 3))
    print('下雪')
    lock.release()


def task3(lock):
    lock.acquire()
    print('你好,阿四。。。')
    time.sleep(random.randint(0, 3))
    print('吃米')
    time.sleep(random.randint(0, 3))
    print('下冰雹')
    lock.release()


if __name__ == '__main__':
    lock=Lock()
    p1=Process(target=task1,args=(lock,))
    p2=Process(target=task2,args=(lock,))
    p3=Process(target=task3,args=(lock,))

    p1.start()
    p2.start()
    p3.start()
View Code
# 注意1:  不要对同一把执行多出acquire 会锁死导致程序无法执行  一次acquire必须对应一次release

from multiprocessing import Lock
l=Lock()
l.acquire()
print('抢到了')
l.release()

l.acquire()
print('接着抢')

# 注意2:想要保住数据安全,必须保住所有进程使用同一把锁
锁和join的区别:

1.  join是固定了执行顺序,会造成父进程等待子进程完成后完成

​	   锁是公平竞争谁先抢到谁先执行,父进程可以做其他事情、

2.  join是把进程的任务全部串行

​	  锁可以锁任意代码 ,一行也可以 可以自己调整粒度

粒度:

粒度越大意味着锁住的代码越多 效率越低
粒度越小意味着锁住的代码越少 效率越高

MYSQL 中不同隔离级别 其实就是不同的粒度  

 小练习 -- 抢票

"""
过程:
1.查看余票,
2.有就买,无失败
 os.getpid()获取当前进程id
#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别

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


def check_ticket():
    with open('ticket.json', 'rt', encoding='utf-8')as a:
        data = json.load(a)
        time.sleep(random.randint(0, 3))
        print('%s正在查票,票还有%s' % (os.getpid(), data["count"]))


def buy_ticket():
    with open('ticket.json', 'rt', encoding='utf-8')as a:
        data = json.load(a)
        if data["count"] > 0:
            data["count"] -= 1
            # 模拟延迟
            time.sleep(random.randint(0, 3))
            with open('ticket.json', 'wt', encoding='utf-8')as a:
                json.dump(data, a)
                print('%s恭喜你抢票成功' % os.getpid())

        else:
            print('%s抢票失败,开个加速包试一试' % os.getpid())


def task(lock):
    check_ticket()
    lock.acquire()
    buy_ticket()
    lock.release()


if __name__ == '__main__':

    lock = Lock()
    # 三个人抢票
    for i in range(10):
        p = Process(target=task, args=(lock,))
        p.start()
抢票

猜你喜欢

转载自www.cnblogs.com/komorebi/p/11348565.html