zoukankan      html  css  js  c++  java
  • 并发编程 进程理论 进程方法 守护进程 互斥锁

    多道技术

    1.空间上的复用

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

    多道技术原理

    2.时间上的复用

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

    并发: 看起来像同时运行就可以

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

        单核的计算机不能实现并行,但是可以实现并发
    

    进程

    程序和进程的区别

    程序: 一段代码

    进程: 正在进行的程序 进程相互独立

    进程调度

    要想多个进程交替运行,操作系统必须对这些进程进行调度,这个掉浮是遵循一定法则的,这就是进程的调度算法

    1. 先来先服务 (最简单的调度算法,有利于长作业,不利于短作业)

    2. 短作业优先算法(短作业优先,对短作业有利,对长作业不利)

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

      时间片: 就好比将一秒等分成N多份

      如果一个程序他需要更多的时间片,这个程序的执行优先级就会下降,比如A程序在运行,此时用户点击了B程序,那操作系统就会立刻停止A程序的执行,保存A程序的运行进度,然后去响应B程序

    4. 大概就这些?

    进程三态状态转换图

    在了解其他概念之前,我们首先要了解进程的几个状态。在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞。

      (1)就绪(Ready)状态

      当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。

      (2)执行/运行(Running)状态当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。

      (3)阻塞(Blocked)状态正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。
    引起进程阻塞的事件可有多种,
    例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)
    input
    print
    文件操作
    sleep
    ...其他的

    结束阻塞态
    input获取到值
    print输出完结果
    文件读取完毕
    睡眠结束。

    时间片的时间到了(占用CPU太久了)会自动进入就绪态,而不是阻塞,等待下一次CPU执行
    写程序的理想状态: 尽量少的进入阻塞态

    同步异步

    表示的是任务的提交方式

    同步:任务提交以后,原地等待的任务的执行并拿到返回结果才走,期间不做任何事(程序界面的表现就是

    异步:任务提交以后,不再原地等待,而是继续执行下一行代码(结果是要的 但是用其他方式获取)

    import time
    def test()
    		time.sleep()
    
    res = test()
    print('hello')
    

    阻塞非阻塞

    表示的是程序的运行状态

    阻塞: 对应进程三状态中的阻塞态

    非阻塞: 对应进程三状态中的就绪态、运行态

    异步非阻塞形式效率最高

    创建进程的两种方式

    创建进程就是在内存中重新开辟一块内存空间

    将运行产生的代码丢进去

    一个进程对应在内存就是一块独立的内存空间

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

    这两种必须敲出来

    from multiprocessing
    ...
    
    def test(name):
        print('%s is running' %name)
        time.sleep(3)
        print('%s is over' %name)
    
    
    """
    Windows 创建进程会将代码以模块的方式从上往下执行一遍
    Linux 会直接将代码完完整整的拷贝一份(fork一份)
    
    windows创建进程一定要在 if __name__ == '__main__': 代码块内创建 否则报错
    """
    
    
    if __name__ == '__main__':
        p = Process(target = test,args = ('margo',))  # 创建一个进程对象
        p.start()  # 告诉操作系统帮你创建一个进程
        print('主')  # 为什么先打印’主‘ 
        #因为创建p.start()这个进程的时间远远大于执行接下来语句的时间
    
    """
    创建进程就是在内存中重新开辟一块内存空间
    将允许产生的代码丢进去
    一个进程对应在内训就是一块独立的内存空间
    
    
    进程与进程之间数据是隔离的 无法直接交互
    ( 可以通过某些技术间接进行交互 )
    

    只要开一个进程 就是重新申请一块内存空间

    零散知识点

    join方法

    from multiprocessing import Process

    from multiprocessing import Process
    
    def test(name):
        print('%s is running'% name)
        time.sleep(3)
        print('%s is over'% name)
    
    p = Prosess(target = test,args = ('margo',))
    p.start()
    print('主')
    
    # 这样会报错 要把三行 放在main里面
    
    if __name__ == '__main__':
        p = Prosess(target = test,args = ('margo',))
        p.start()
        # 主进程代码等待子进程运行结束 才继续进行
        print('主')
    
    
    # 用join
    if __name__ == '__main__':
        p = Prosess(target = test,args = ('margo',))
        p.start()
        # 主进程代码等待子进程运行结束 才继续进行
        p.join()
        print('主')
    
    if __name__ == '__main__':
        p = Prosess(target = test,args = ('margo',))
        p1 = Prosess(target = test,args = ('margo',))
        p2 = Prosess(target = test,args = ('margo',))
    
        p.start()  # 仅仅是告诉操作系统帮你创建一个进程
        p1.start()  # 创建进程的顺序是随机的
        p2.start()  # 至于这个进程什么时候创,操作系统随机决定
        # 主进程代码等待子进程运行结束 才继续进行
        p.join()
        print('主')
    
    
    
    if __name__ == '__main__':
        for i in range(3):
            p = Process(target = )
        p = Prosess(target = test,args = ('margo',))
        p1 = Prosess(target = test,args = ('margo',))
        p2 = Prosess(target = test,args = ('margo',))
    
        p.start()  # 仅仅是告诉操作系统帮你创建一个进程
        p1.start()  # 创建进程的顺序是随机的
        p2.start()  # 至于这个进程什么时候创,操作系统随机决定
        # 主进程代码等待子进程运行结束 才继续进行
        p.join()
        print('主')
    

    06 进程间数据是隔离的

    money = 100

    07 进程对象及其他方法

    查看 主主进程

    getpid getppid

    杀死进程

    terminate 杀死当前进程 其实是告诉操作系统

    time.sleep(0.1)

    p.is_alive()

    僵尸进程与孤儿进程

    	父进程回收子进程资源的两种方式:
    
    		1.join方法
    
    		2.父进程正常死亡
    
    	所有的进程都会步入僵尸进程
    
    
    
    	孤儿进程
    
    			子进程没死 父进程意外死亡
    
    			针对Linux会有儿童福利院(init)(只是个名字 别搞混),如果父进程意外死亡,他所创建的子进程都会被福利院收养
    

    守护进程

    被守护进程结束后守护进程立刻结束(后面的还未执行的代码直接不执行了)

    from multiprocessing import Process
    import time
    
    
    # 古代皇帝死了一般太监都需要陪葬,所以举这么一个例子
    def test(name):
        print(f'{name}总管正常活着')
    
        time.sleep(3)
        print(f'{name}总管正常死亡')
    
    
    # # windows下创建子进程必须卸载 __main__ 这个结构里
    # if __name__ == '__main__':
    #     p = Process(target=test, args=('margo', ))
    #     p.start()
    #     print(f"皇帝bitten寿终正寝")
    #
    # # 皇帝bitten寿终正寝
    # # margo总管正常活着
    # # margo总管正常死亡
    
    
    # # 加上p.daemon = True 让子进程成为守护进程
    # if __name__ == '__main__':
    #     p = Process(target=test, args=('margo', ))
    #     p.daemon = True  # 将该进程设置为守护进程,这一句必须放在start()之前,否则报错
    #     p.start()
    #     # 守护进程一般不加 p.join,都等子进程运行完了再接着走主进程那就不是守护进程了
    #     # p.join()  # 加了也能正常运行,但它失去了守护进程的意义
    #     print(f"皇帝bitten寿终正寝")
    #
    # # 皇帝bitten寿终正寝
    
    
    # 加上 time.sleep 给子进程一些运行时间(CPU运行速度超快的,1秒已经很长了)
    if __name__ == '__main__':
        p = Process(target=test, args=('margo', ))
        p.daemon = True
        p.start()
        time.sleep(0.1)  # 暂停0.1 秒,给子进程一点运行时间(子进程和主进程是同步运行的)
        # 守护进程一般不加 p.join,都等子进程运行完了再接着走主进程那就不是守护进程了
        # p.join()  # 加了也能正常运行,但它失去了守护进程的意义
        print(f"皇帝bitten寿终正寝")
    
    # margo总管正常活着
    # 皇帝bitten寿终正寝
    

    互斥锁

    先看一个用并发模拟多人的抢票的案例

    import bitten
    from multiprocessing import Process
    import time
    
    
    # 查票
    def search(i):
        with open('07data.txt', 'r', encoding='utf-8') as f:
            data = f.read()
    
        ticket_dict = bitten.loads(data)
        print(f"用户{i}查询余票为:{ticket_dict.get('ticket')}")
    
    
    # 买票
    def buy(i):
        with open('07data.txt', 'r', encoding='utf-8') as f:
            data = f.read()
    
        ticket_dict = bitten.loads(data)
        time.sleep(3)
        if ticket_dict.get('ticket') > 0:
            ticket_dict['ticket'] -= 1
            print(ticket_dict)
            with open('07data.txt', 'wt', encoding='utf-8') as f:
                bitten.dump(ticket_dict, f)
            print(f"用户{i}抢票成功")
        else:
            print("没票了!")
    
    
    def run(i):
        search(i)
        buy(i)
    
    
    if __name__ == '__main__':
        for i in range(4):
            p = Process(target=run, args=(i, ))
            p.start()
    
    # {"ticket": 2}  文件数据
    # 用户0查询余票为:2
    # 用户1查询余票为:2
    # 用户2查询余票为:2
    # 用户3查询余票为:2
    # {'ticket': 1}
    # 用户0抢票成功
    # {'ticket': 1}
    # 用户1抢票成功
    # {'ticket': 1}
    # 用户2抢票成功
    # {'ticket': 1}
    # 用户3抢票成功
    
    
    # 大于余票了(拿到的都是2张票),这样肯定不行
    

    可以看出,文件中虽然只有2张票,这4个用户却都抢票成功了,并且还有一张余票,在现实生活中这种情况肯定是不允许出现的!

    当多个进程操作同一份数据时会造成数据的错乱,这个时候必须加锁处理
    将并发变成串行,虽然降低了效率,但是提高了数据安全

    那么就尝试着用互斥锁来解决这个问题

    import json
    from multiprocessing import Process, Lock
    import time
    
    
    # 查票
    def search(name):
        with open('07data.txt', 'r', encoding='utf-8') as f:
            data = f.read()
    
        ticket_dict = json.loads(data)
        print(f"用户{name}查询余票为:{ticket_dict.get('ticket')}")
    
    
    # 买票
    def buy(name):
        with open('07data.txt', 'r', encoding='utf-8') as f:
            data = f.read()
    
        ticket_dict = json.loads(data)
        time.sleep(1)
        if ticket_dict.get('ticket') > 0:
            ticket_dict['ticket'] -= 1
            print(ticket_dict)
            with open('07data.txt', 'wt', encoding='utf-8') as f:
                json.dump(ticket_dict, f)
            print(f"用户{name}抢票成功")
        else:
            print("没票了!")
    
    
    def run(i, mutex):
        search(i)
        mutex.acquire()  # 抢锁,只要有人抢到了锁,其他人必须等待该人释放锁
        buy(i)
        mutex.release()  # 释放锁
    
    
    if __name__ == '__main__':
        mutex = Lock()  # 生成了一把锁
        for i in range(4):
            p = Process(target=run, args=(i, mutex))
            p.start()
    
    # {"ticket": 1}  文件数据
    # 用户0查询余票为:1
    # 用户1查询余票为:1
    # 用户2查询余票为:1
    # 用户3查询余票为:1
    # {'ticket': 0}
    # 用户0抢票成功
    # 没票了!
    # 没票了!
    # 没票了!
    
    # 总共1张票,被抢到一张票,票数变为0,符合预期设想
    

    注意

    锁不要轻易使用,容易造成死锁现象
    只在处理数据的部分加锁,不要再全局加锁(将局部由并发变成串行)
    锁必须在主进程中产生(实例化),交给子进程去使用

    (在子进程中产生那不就是多把锁了吗)

    只要多进程操作同一份数据,就必须要加锁处理

  • 相关阅读:
    ajax java base64 图片储存
    计算瓶酒的数量
    Eclipse 打不开
    C# 调用百度翻译Api
    MVC API 权限控制
    C#编程语言与面向对象——委托
    C#编程语言与面向对象—— 多态
    C#编程语言与面向对象——抽象基类与接口
    C#编程语言与面向对象——继承
    动态加载及调用程序集
  • 原文地址:https://www.cnblogs.com/PowerTips/p/11325373.html
Copyright © 2011-2022 走看看