zoukankan      html  css  js  c++  java
  • 并发编程(一)操作系统基础和进程概念

    1.操作系统基础知识

    一.操作系统的作用

    1.隐藏丑陋复杂的硬件接口,提供良好的抽象接口

    2.管理、调度进程,并且将多个进程对硬件的竞争变得有序

    二.多道技术

    .空间上的复用

      多个程序共用一套计算机硬件

    2.时间上的复用

      切换+保存状态

      1.当一个程序遇到I/O操作时,操作系统会剥夺该程序的cpu执行权限(提高cpu的利用率,也不会影响程序执行效率)

      2.当一个程序长时间占用cpu,操作系统会剥夺该程序的cpu执行权限(降低了程序的执行效率)

    img

    2.进程基本概念

    什么是进程

      程序就是一串代码,进程就是正在运行的程序,它是资源分配和调度的基本单位,是操作系统结构的基础.

    进程调度的算法

      1.先来先服务调度算法

      2.短作业优先调度算法

      3.时间片轮转法

      4.多级反馈队列

      第一种和第二种算法都有明显的缺点,第一种对短作业不友好,第二种对长作业不友好,所以现代计算机进程都是基于第三种和第四种方法实现进程,如下图所示

    img

    进程的并行和并发

      并发:看起来像同时运行

      并行:真正意义上的同时执行

      注意:单核计算机不能实现并行,但可以实现并发,所以要实现并行必须有多个处理器

    进程的三种状态

      1.就绪态:

        当进程已经做好了所有准备,就等cpu执行了,此时的状态就是就绪态

      2.运行态

        当进程正在被cpu执行的时候,此时的状态就是执行态

      3.阻塞态:

        当进程在被执行的过程中发生了一些必要的I/O操作无法继续执行时,cpu会放弃该进程转而执行其他进程,此时的状态就是阻塞态

    img

    同步异步

      同步就是一个任务的完成必须等待另一个任务完成后才能算完成(类似于排队,程序的层面就是卡住了)

      异步就是一个任务在执行的完成不需要知道另一个任务是否完成,只要自己的任务完成就算完成了(类似于叫号,另一个任务的完成结果是要的,但是是通过其他方式获取)

    阻塞非阻塞  

      阻塞就是进程不在执行,处于阻塞态

      非阻塞就是进程还在执行,处于就绪态或者运行态

    两者组合成为四种状态

      1.同步阻塞:只能做一件事情,并且处于阻塞态,效率最低

      2.异步阻塞:因为等待某个事件而处于阻塞态,但是他不依赖于其他任务,所以事件处理完成就可以了

      3.同步非阻塞:需要依赖于另一个任务的完成,就算自己的任务先完成了,但是还得等其他任务,效率也是地下的

      4.异步非阻塞:不需要依赖于其他任务,自己又是非阻塞状态,可想而知效率是最高的

    3.创建进程的两种方式

    在python中创建进程需要导入一个multiprocess模块,他是python中操作、管理进程的一个包,他包含了和进程有关的所有子模块

    multiprocess.process模块

    process模块是一个创建进程的模块

    创建进程就是在内存中重新开辟一块属于进程的独立的内存空间

    进程和进程之间的数据是隔离的,无法直接交互,但是可以通过某些技术实现间接交互

    创建进程的第一种方式

    p=process(target=函数名,args=(,))
    p.start()

    from multiprocessing import Process
    import time
    
    age = 18
    
    def run(msg):
        global age
        age = 28
        print('%s开始'%msg)
        print(age)  # 子进程中的是28,两者无法访问
        time.sleep(1)
        print('%s结束'%msg)
    
    '''
    
    windows会在进程创建时会以模块的方式从上到下的执行一遍,
    所以在windows中创建进程一定要在if __name__ == '__main__':代码块中创建
    linux中创建进程会直接复制一份
    '''
    if __name__ == '__main__':
        p = Process(target=run,args=('子进程',))  # 生成一个进程对象
        p.start()  # 创建一个进程
        print(age)  # 主进程中的age是18
        print('主进程')  # 主进程中的内容
    

    执行顺序:进程的创建(创建内存空间)的速度慢于print的速度,所以先执行下面

    创建进程的第二种方式

    类实例化
    p=Myprocess(参数)
    p.start()

    from multiprocessing import Process
    import time
    age = 18
    # 新建一个类继承父类Process
    class MyProcess(Process):
        def __init__(self,msg):
            super().__init__()
            self.msg = msg
    
        def run(self):
            global age
            age = 28
            print('%s开始'%self.msg)
            print(age)
            time.sleep(1)
            print('%s结束'%self.msg)
            
    if __name__ == '__main__':
        p = MyProcess('子进程',)
        p.start()
        print(age)
        print('主进程')
    

    4.进程方法join(暂停)

      在上述创建进程代码中,父进程执行完毕之后才会执行子进程,其实两者并没有先后的关系,执行的顺序是操作系统来决定的,并且生成子进程也会消耗一段时间,故子进程在父进程之后,如果我们想要实现子进程完成之后在执行父进程,则需要join方法(*****)

    from multiprocessing import Process
    import time
    
    def run(msg,i):
        print('%s开始'%msg)
        time.sleep(i)
        print('%s结束'%msg)
    
    if __name__ == '__main__':
        start = time.time()  # 记录开始时间
        p = Process(target=run,args=('子进程',0))
        p1 = Process(target=run,args=('子进程1',1))
        p2 = Process(target=run,args=('子进程2',2))
        p3 = Process(target=run,args=('子进程3',3))
        p.start()
        p1.start()
        p2.start()
        p3.start()
        p.join()  # 加入该方法后子进程会先执行完毕
        p1.join()
        p2.join()
        p3.join()
        end = time.time()  # 记录结束时间
        print('父进程')
        print(end - start)  # 打印总共的运行时间
    


    总结JOIN的作用:进程的执行顺序仍然不固定,然是加入join的进程,会等改进程执行完之后再执行非进程之外的代码

    5.证明进程间数据隔离

    from multiprocessing import Process
    age = 18
    
    def run():
        global age
        age = 28
        print('子进程的age:%s'%age)
    
    if __name__ == '__main__':
        p = Process(target=run)
        p.start()
        p.join()
        print('主进程的age:%s' % age)
    

    print打印的结果与进程结果不一致,证明:global并没有将函数内的数据全局化

    6.进程对象及其他方法

    1、获取查看当前进程号

    current_process().pid(该方法不好用(不知道哪里不好用),os也有相同的方法):

    import time
    from multiprocessing import Process, current_process
    
    def test(name):
        print('%s is running'%name,current_process().pid)#1336
        time.sleep(3)
        print('%s is over' % name )
    
    if __name__ == '__main__':
        p=Process(target=test,args=('wx',))
        p.start()
        print('主',current_process().pid)#2748
    '''
    主 1336
    wx is running 2748
    wx is over
    '''
    

    2、os里的获取当前进程的进程号

    os.getpid:可以查看当前进程的pid(进程id)

    os.getppid:可以查看当前进程的父进程的pid(进程id)

    p.terminate强制终止当前进程
    p.is_alive():判断子进程p是否还存活
    if __name__ == '__main__':
        p=Process(target=test,args=('wx',))
        p.start()
        p.terminate()
        time.sleep(0.1)#必须加时间等待
        print(p.is_alive())#False
        print('主',os.getpid())
    

    7.僵尸进程和孤儿进程(了解)

    子进程结束后:子进程号,占用cpu时间信息依然占用,不会立即释放,直到主进程回收

    僵尸进程就是子进程死亡后,父进程还存放着子进程号,占用cpu时间信息,一旦僵尸进程过多,会占用系统资源

      父进程回收死亡的子进程(僵尸进程)资源的两种方式

        1.join方法(调用wait)方法

        2.父进程正常死亡(所有子进程运行完)

        注意:所有进程都会步入僵尸进程

    孤儿进程就是子进程没死,父进程意外死亡

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

    8.守护进程

    格式:进程.deamon=True。写在start前面
    守护进程代码运行到print代码,那么就不运行子进程代码

    后面会降到:守护进程和守护线程的不同

    主进程创建守护进程

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

      守护进程内无法再开启子进程,否则就会抛出异常

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

    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.5)
        print('皇帝jason寿正终寝')
    

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

    8.互斥锁

    一个简易的没有互斥锁的抢票软件

    import json
    from multiprocessing import Process
    import time
    
    # 查票
    def search(i):
        with open('ticket','r',encoding='utf-8') as f:
            json_dict = f.read()
            dict = json.loads(json_dict)
        print('查 用户%s余票还有%s'%(i,dict.get('ticket')))
    
    # 买票
    def buy(i):
        with open('ticket','r',encoding='utf-8') as f:
            json_dict = f.read()
            dict = json.loads(json_dict)
        print('买 用户%s余票还有%s' % (i, dict.get('ticket')))
        time.sleep(1)
        if dict.get('ticket') > 0 :
            dict['ticket'] -= 1
            with open('ticket','w', encoding='utf-8')as f:
                d = json.dumps(dict)
                f.write(d)
            print('用户%s抢票成功'%i)
        else:
            print('用户%s卖完了'%i)
    def run(i):
        search(i)
        buy(i)
    
    if __name__ == '__main__':
        for i in range(1,4):
            p = Process(target=run,args=(i,))
            p.start()
    

    现在产生了一个问题,网络有延迟,这么多用户在time.sleep的时间内,还没等第一个人刷新购票结果,其他人已经查询完余票了。因此导致所有人都能该买到票、

    加锁

    import json
    from multiprocessing import Process,Lock
    import time
    
    def search(i):
        with open('ticket','r',encoding='utf-8') as f:
            json_dict = f.read()
            dict = json.loads(json_dict)
        print('查 用户%s余票还有%s'%(i,dict.get('ticket')))
    
    def buy(i):
        with open('ticket','r',encoding='utf-8') as f:
            json_dict = f.read()
            dict = json.loads(json_dict)
        print('买 用户%s余票还有%s' % (i, dict.get('ticket')))
        time.sleep(1)
        if dict.get('ticket') > 0:
            dict['ticket'] -= 1
            with open('ticket','w', encoding='utf-8')as f:
                d = json.dumps(dict)
                f.write(d)
                print('用户%s抢票成功'%i)
        else:
            print('用户%s卖完了'%i)
    def run(i,mutex):
        search(i)
        mutex.acquire()
        buy(i)
        mutex.release()
    
    if __name__ == '__main__':
        mutex = Lock()
        for i in range(1,4):
            p = Process(target=run,args=(i,mutex))
            p.start()
    
  • 相关阅读:
    第二月 day 2,内置函数
    第二月 day3 闭包,递归
    day4 装饰器
    第二月 day1生成器
    第一个月 总结
    day 16 迭代器
    day 15 编码
    Docker常用命令
    DRF源码刨析
    django中使用qiniu作为第三方存储
  • 原文地址:https://www.cnblogs.com/ZDQ1/p/11346703.html
Copyright © 2011-2022 走看看