创建进程,join方法,进程对象相关属性和方法,僵尸进程和孤儿进程,守护进程,互斥锁

创建进程

    在python中提供了一个multiprocessing模块可以帮助我们使用多进程解决问题。在multiprocessing
模块中有一个类Process。
    from multiprocessing import Process

'''
    group=None, 为日后开发新功能准备 
    target=None, 目标任务
    name=None, 进程的姓名
    args=(), 目标任务需要的位置参数
    kwargs={}, 目标任务需要的字典参数
    daemon=None):默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
    '''
    #方法介绍
    p.start()#开启进程,使用run()方法
    p.run()#进程启动时运行的方法是它去调用target指定的目标
    p.terminate()#强制终止进程,不会进行清理操作
    p.is_alive()#判断进程是否存活
    p.join([timeout])#让在join方法之后的进程等待,直到p进程运行结束

"""
强调:不同的操作系统创建进程的要求不一样
    在windows中创建进程是以导入模块的方式进行 所以创建进程的代码必须写在__main__子代码中
    否则会直接报错 因为在无限制创建进程
    在linux和mac中创建进程是直接拷贝一份源代码然后执行 不需要写在__main__子代码中
"""

第一种方法

    from multiprocessing import Process
    import time


    def test():
        print('=======>')
        time.sleep(3)
        print('=======>')


    def test1(name):
        print(f'{name}')
        time.sleep(3)
        print('=======>')


    if __name__ == '__main__':
        p = Process(target=test)
        # p = Process(target=test1, args=('春游去动物园',))
        p.start()
        print('主')

第二种方法

    from multiprocessing import Process

    class Test(Process):
        def __init__(self,name):
            super().__init__()
            self.name = name


        def run(self):
            print(self.name)


    if __name__ == '__main__':
        p = Test('test')
        p.start()
        print('主')
    #自定义类主要是改写他的run()方法,run()方法在调用start()方法时一定会被调用

join

'''join让主进程等待子进程结束之后,再执行主进程。'''

    from multiprocessing import Process
    import time
    def task(name):
        print(f"{name} is running")
        time.sleep(2)
        print(f"{name} is gone")
    if __name__ == "__main__":
        p = Process(target=task, args=("春游去动物园",))  # 创建一个进程对象
        p.start()
        # p.join()
        print(111)
        print("主开始")

    111
    主开始
    春游去动物园 is running
    春游去动物园 is gone


    from multiprocessing import Process
    import time
    def task(name):
        print(f"{name} is running")
        time.sleep(2)
        print(f"{name} is gone")
    if __name__ == "__main__":
        p = Process(target=task, args=("春游去动物园",))  # 创建一个进程对象
        p.start()
        p.join()
        print(111)
        print("主开始")
        
    春游去动物园 is running
    春游去动物园 is gone
    111
    主开始
'''一般而言,主程序中如果单为一句print,则优先执行print语句(如果执行语句够多,则可见子进程执行)'''

    from multiprocessing import Process
    import time

    def task(name):
        print(f"{name} is running")
        time.sleep(2)
        print(f"{name} is gone")

    if __name__ == "__main__":
        p = Process(target=task,args=("春游去动物园",))  # 创建一个进程对象
        p.start()
        # p.join()
        print(111)
        time.sleep(0.5)
        print("主开始")
        
    111
    春游去动物园 is running
    主开始
    春游去动物园 is gone
'''如果程序中有连续多个join函数,则只有最先的join是起作用的'''
    from multiprocessing import Process
    import time

    def task(name,sec):
        print(f"{name} is running")
        time.sleep(sec)
        print(f"{name} is gone")

    if __name__ == "__main__":
        start_time = time.time()
        p = Process(target=task,args=("常",3))  # 创建一个进程对象
        p1 = Process(target=task,args=("辛",5))  # 创建一个进程对象
        p2 = Process(target=task,args=("王",1))  # 创建一个进程对象

        p.start()
        p1.start()
        p2.start()

        # join只针对主进程,如果join下面多次join他是不阻塞的
        p.join()
        p1.join()
        p2.join()

        print(f"主{time.time()-start_time}")

    辛 is running
    常 is running
    王 is running
    王 is gone
    常 is gone
    辛 is gone
    主5.103694438934326



    # 注:如果不是连续多个,比如这样:
    from multiprocessing import Process
    import time

    def task(name,sec):
        print(f"{name} is running")
        time.sleep(sec)
        print(f"{name} is gone")

    if __name__ == "__main__":
        start_time = time.time()
        p = Process(target=task,args=("常",3))  # 创建一个进程对象
        p1 = Process(target=task,args=("辛",5))  # 创建一个进程对象
        p2 = Process(target=task,args=("王",1))  # 创建一个进程对象

        p.start()
        p.join()
        p1.start()
        p1.join()
        p2.start()
        p2.join()

        print(f"主{time.time()-start_time}")

    常 is running
    常 is gone
    辛 is running
    辛 is gone
    王 is running
    王 is gone
    主9.267089128494263
    # 这样就会先执行p进程,再执行p1进程,再执行p2进程

进程间数据默认隔离

    from multiprocessing import Process

    a=100
    def test():
        global a
        a=50
        print(a) # 50


    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(a) # 100

    100
    50

进程对象属性和方法

查看进程号

    '''第一种current_process函数'''
    from multiprocessing import Process,current_process
    a=100
    def test():
        global a
        a=50
        print(a) # 50
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(current_process()) # 获取进程的名称 <_MainProcess name='MainProcess' parent=None started>
        print(current_process().pid) # 获取进程的id 8560
        print(a) # 100

    '''第二种os模块'''
    import os
    from multiprocessing import Process,current_process
    a=100
    def test():
        global a
        a=50
        print(a) # 50
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(os.getpid()) # 9748 获取当前进程的进程号
        print(os.getppid()) # 5220 获取当前进程的父进程号
        print(a) # 100


    os.getpid()  # 获取当前进程的进程号
    os.getppid()  # 获取当前进程的父进程号

杀死子进程

    terminate():不管任务是否完成,立即终止子进程
    from multiprocessing import Process,current_process
    import time
    a=100
    def test():
        global a
        time.sleep(3)
        a=50
        print(a)
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        p.terminate()
        print(a)
    100

判断子进程是否存活

    is_alive():判断进程子进程是否还在活着
    from multiprocessing import Process,current_process
    import time
    a=100
    def test():
        global a
        time.sleep(3)
        a=50
        print(a)
    if __name__ == '__main__':
        p = Process(target=test)
        p.start()
        print(p.is_alive()) # True
        p.terminate() # 杀死进程
        time.sleep(0.5)
        print(p.is_alive()) # False

僵尸进程与孤儿进程

僵尸进程

  僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子
进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
    '''
        僵死进程:子进程退出后,会将该进程的重型资源释放掉(cpu,内存,打开的文件),子进程的进
程描述符仍然保存在系统中,比如pid。
    '''
    所有的子进程在运行结束之后都会变成僵尸进程(死了没死透)
    程序正常结束才会产生僵尸进程,如果强制关闭父进程,操作系统会把父进程已经运行结束的子进程全部
删除,也就不会产生僵尸进程了。
    僵尸进程的危害:
      系统的pid号是有限的,僵尸进程保留的信息如果一直不被释放,一直累计会导致没有可用的pid号而
导致系统不能产生新的进程

孤儿进程

   孤儿进程(无害):一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿
进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
   '''
      子进程存活着 父进程意外死亡
      子进程会被操作系统自动接管(儿童福利院)
   '''

守护进程

    正常情况下,主进程默认等待子进程调用结束之后结束
    守护进程在主进程执行代码结束后,自动终止
    """
        守护即死活全部参考守护的对象
	对象死立刻死
    """
    主进程代码运行完毕,守护进程就会结束
    from multiprocessing import Process
    import os, time


    def task():
        print("进程%s开启" % os.getpid())
        time.sleep(10)
        print("进程%s结束" % os.getpid())


    if __name__ == '__main__':
        p = Process(target=task,daemon=True) # 在创建进程时也可以设置daemon,True为开启守护进程,默认为False。
        #p.daemon = True   	# 这一行代码会把子进程变成守护代码,主进程运行完,子进程也就运行完了,不会打印进程结束的那行代码
        p.start()
        print("主:%s" % os.getpid())
        time.sleep(3)

        主:6812
        进程10096开启

互斥锁

互斥锁的概念

    互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。

    注意:
      互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放
后,其它等待的线程再去抢这个锁。

    互斥锁的意思就是互相排斥,我们可以把多个进程比喻多个人,互斥锁的工作原理就是多个人去争抢共同
的一个资源:如多个人要上同一个卫生间,一个人抢到卫生间后上一把锁,其他人都有外面等着,等到这个人
完成后解锁后,其他人又可以去争夺。所以互斥锁的原题,就是把某一功能并发改串行,虽然降低了效率,但
保证了数据安全不错乱。
    """
    锁相关知识
    	行锁:针对行数据加锁 同一时间只能一个人操作
    	表锁:针对表数据加锁 同一时间只能一个人操作
    锁的应用范围很广 但是核心都是为了保证数据的安全!!!
    """

互斥锁的使用

    from multiprocessing import Process,Lock
    # 创建锁
    mutex = Lock()

    # 上锁
    mutex.acquire()

    ...这里编写代码能保证同一时刻只能有一个线程去操作, 对共享数据进行锁定...

    # 释放锁
    mutex.release()

    注意点:
    acquire和release方法之间的代码同一时刻只能有一个线程去操作
    如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到
这个互斥锁释放后才能再次上锁。

互斥锁与join()的区别

    互斥锁与join()的区别:
      大前提:二者的原理都是一样,都是将并发变成串行,从而保证有序

      区别一:join是按照人为指定的顺序执行,而互斥锁是所有进程平等地竞争,谁先抢到谁先执行。

      区别二:互斥锁可以让一部分代码(修改共享数据的代码)串行,而join只能将代码整体串行。

抢票小案例

    # db.json文件中的内容为{"count": 100}
    from multiprocessing import Process, Lock
    import json, time
    import random


    def search(name):
        dic = json.load(open('db.json', 'r', encoding='utf-8'))
        time.sleep(random.randint(0, 2))  # 模拟网络延迟
        print('%s查到剩余的票数为%s张' % (name, dic['count']))


    def get(name):
        dic = json.load(open('db.json', 'r', encoding='utf-8'))
        time.sleep(0.3)  # 模拟网络延迟
        if dic['count'] > 0:
            time.sleep(0.1)  # 模拟网络延迟
            print('%s成功买到了剩下的第%s票' % (name, dic['count']))
            dic['count'] -= 1
            json.dump(dic, open('db.json', 'w', encoding='utf-8'))


    def rob_ticket(name, lock):
        search(name)
        with lock:  # 相当于获得了lock.acquire(),执行代码体完,自动执行lock.release()
            get(name)


    if __name__ == '__main__':
        lock = Lock()
        for i in range(10):
            name = '路人%s' % i
            p = Process(target=rob_ticket, args=(name, lock))
            p.start()
            
    执行结果:
    路人0查到剩余的票数为5张
    路人1查到剩余的票数为5张
    路人8查到剩余的票数为5张
    路人7查到剩余的票数为5张
    路人9查到剩余的票数为5张
    路人0成功买到了剩下的第5票
    路人1成功买到了剩下的第4票
    路人2查到剩余的票数为5张
    路人4查到剩余的票数为5张
    路人3查到剩余的票数为5张
    路人6查到剩余的票数为5张
    路人8成功买到了剩下的第3票
    路人7成功买到了剩下的第2票
    路人5查到剩余的票数为5张
    路人9成功买到了剩下的第1票
posted @ 2022-04-19 21:50  春游去动物园  阅读(42)  评论(0编辑  收藏  举报