multiprocess(上)

 

仔细说来,multiprocess不是一个模块而是python中一个操做、管理进程的包。 之因此叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的全部子模块。因为提供的子模块很是多,为了方便你们归类记忆,我将这部分大体分为四个部分:建立进程部分,进程同步部分,进程池部分,进程之间数据共享。重点强调:进程没有任何共享状态,进程修改的数据,改动仅限于该进程内,可是经过一些特殊的方法,能够实现进程之间数据的共享。html

  • Process模块:一个建立进程的模块,借助这个模块,就能够完成进程的建立。node

     
     
     
    x
     
     
     
     
    #Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化获得的对象,表示一个#子进程中的任务(还没有启动)
    #强调:
    #1. 须要使用关键字的方式来指定参数
    #2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
    # 当前文件名称为test.py
    from multiprocessing import Process
    def func():
        print(12345)
    if __name__ == '__main__': #windows 下才须要写这个,这和系统建立进程的机制有关系,不用深究,记着windows下要写就好啦(由于windows是经过import导入模块,因此若是不区分那么会无限导入,而mac、linux则是直接copy导入文件的方式,首先我运行当前这个test.py文件,运行这个文件的程序,那么就产生了进程,这个进程咱们称为主进程
        p = Process(target=func,) #将函数注册到一个进程中,p是一个进程对象,此时尚未启动进程,只是建立了一个进程对象。而且func是不加括号的,由于加上括号这个函数就直接运行了对吧。
        p.start() #告诉操做系统,给我开启一个进程,func这个函数就被咱们新开的这个进程执行了,而这个进程是我主进程运行过程当中建立出来的,因此称这个新建立的进程为主进程的子进程,而主进程又能够称为这个新进程的父进程。 而这个子进程中执行的程序,至关于将如今这个test.py文件中的程序copy到一个你看不到的python文件中去执行了,就至关于当前这个文件,被另一个py文件import过去并执行了。
    #   start并非直接就去执行了,咱们知道进程有三个状态,进程会进入进程的三个状态,就绪,(被调度,也就是时间片切换到它的时候)执行,阻塞,而且在这个三个状态之间不断的转换,等待cpu执行时间片到了。
        print('*' * 10) #这是主进程的程序,上面开启的子进程的程序是和主进程的程序同时运行的,咱们称为异步
        
    import os
    import time
    from multiprocessing import Process
    def func(a,b,c):
        time.sleep(1)
        print(a,b,c,os.getpid(),os.getppid())     #1 2 3 9936 9592
        # pid : processid   ppid : parent process id
    if __name__ == '__main__':
        # windows操做系统下开启子进程子进程中的代码是经过import这种方式被导入到子进程中的
        print('主 :', os.getpid())         #主 : 9592
        Process(target=func,args=(1,2,3)).start()
        
    import time
    import os
    #os.getpid() 获取本身进程的ID号
    #os.getppid() 获取本身进程的父进程的ID号
    from multiprocessing import Process
    def func():
        print('aaaa')
        time.sleep(1)
        print('子进程>>',os.getpid())  # 8064
        print('该子进程的父进程>>',os.getppid())  # 3552
        print(12345)
    if __name__ == '__main__':
        #首先我运行当前这个文件,运行的这个文件的程序,那么就产生了主进程
        p = Process(target=func,)
        p.start()
        print('*' * 10) 
        print('父进程>>',os.getpid())     #3552
        print('父进程的父进程>>',os.getppid())  #10804
        
    #将上个例题的函数下加 print()后会打印两次,由于子进程会加载主程序的函数,
    #一、也就是说在window是系统上,python经过import导入的形式建立子进程,若是不写if __name__ 语句,则会无限导入
    #二、if 上面的语句在子进程建立时,会在执行一遍
    def func():
        print('aaaa')
        time.sleep(1)
        print('子进程>>',os.getpid())
        print('该子进程的父进程>>',os.getppid())
        print(12345)
    print('太白老司机~~~~') #若是我在这里加了一个打印,你会发现运行结果中会出现两次打印出来的太白老司机,由于咱们在主进程中开了一个子进程,子进程中的程序至关于import的主进程中的程序,那么import的时候会不会执行你import的那个文件的程序啊,前面学的,是会执行的,因此出现了两次打印
    #实际上是由于windows开起进程的机制决定的,在linux下是不存在这个效果的,由于windows使用的是process方法来开启进程,他就会拿到主进程中的全部程序,而linux下只是去执行我子进程中注册的那个函数,不会执行别的程序,这也是为何在windows下要加上执行程序的时候,
    #要加上if __name__ == '__main__':,不然会出现子进程中运行的时候还开启子进程,那就出现无限循环的建立进程了,就报错了
     
  • 咱们能够打开windows的任务管理器查看pycharm的pid的进程号:python

    img

  • 一个主进程运行完了以后,咱们把pycharm关了,可是子进程尚未执行结束,那么子进程还存在吗?这要看你的进程是如何配置的,若是说咱们没有配置说我主进程结束,子进程要跟着结束,那么主进程结束的时候,子进程是不会跟着结束的,他会本身执行完,若是我设定的是主进程结束,子进程必须跟着结束,那么就不会出现单独的子进程(孤儿进程)了,具体如何设置,看下面的守护进程的讲解。好比说,咱们未来启动项目的时候,可能经过cmd来启动,那么我cmd关闭了你的项目就会关闭吗,不会的,由于你的项目不能中止对外的服务,对吧,可是全部的孤儿进程会随着关机,内存释放。linux

  • Process类中各方法的介绍:web

    •  
       
       
      xxxxxxxxxx
       
       
       
       
      一、 p.start():启动进程,并调用该子进程中的p.run()
      二、 p.run():进程启动时运行的方法,正是它去调用target指定的函数,咱们自定义类的类中必定要实现该方法 。
      三、 p.terminate():强制终止进程p,不会进行任何清理操做,若是p建立了子进程,该子进程就成了僵尸进程,使用该方法须要特别当心这种状况。若是p还保存了一个锁那么也将不会被释放,进而致使死锁
      四、 p.is_alive():若是p仍然运行,返回True
      五、 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,须要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程。
      六、start terminate join分别是什么类型?
          start\terminate 异步非阻塞
          join 同步阻塞
       
  • Process类中自带封装的各属性的介绍算法

     
     
     
    xxxxxxxxxx
     
     
     
     
    1 p.daemon:默认值为False,若是设为True,表明p为后台运行的守护进程,当p的父进程(代码终止,而不是主进程终止,由于主进程要进行垃圾回收子进程)终止时,p也随之终止,而且设定为True后,p不能建立本身的新进程,必须在p.start()以前设置
    2 p.name:进程的名称,这些属性是在Process类中的init方法中实现的
    3 p.pid:进程的pid
    4 p.exitcode:进程在运行时为None、若是为–N,表示被信号N结束(了解便可)
    5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络链接的底层进程间通讯提供安全性,这类链接只有在具备相同的身份验证键时才能成功(了解便可)
     
  • join方法的列子:让主进程加上join的地方等待(也就是阻塞住),等待子进程执行完以后,再继续往下执行个人主进程,好多时候,咱们主进程须要子进程的执行结果,因此必需要等待。join感受就像是将子进程和主进程拼接起来同样,将异步改成同步执行。windows

     
     
     
    xxxxxxxxxx
     
     
     
     
    import time
    import os
    from multiprocessing import Process
    def func(x,y):  #前面的省略
        print(x)
        time.sleep(1)
        print(y)
    if __name__ == '__main__':
        p = Process(target=func,args=('姑娘','来玩啊!'))
        p.start()
        print('我这里是异步的啊!')  #这里相对于子进程仍是异步的
        p.join()       #只有在join的地方才会阻塞住,将子进程和主进程之间的异步改成同步
        print('父进程执行结束!')
    #打印结果:
    我这里是异步的啊!
    姑娘
    来玩啊!
    父进程执行结束!
    #第二种传参
    def func(a, b, c):
        print("函数func的参数分别是>>>", a, b, c) 
        print("子进程执行完毕!")
    if __name__ == '__main__':
        p = Process(target=func, kwargs={"a":"参数一", "b":"参数二", "c":"参数三"})
        p.start()
        time.sleep(1)
        print("主进程执行完毕!")
    # 执行结果:
    # 函数func的参数分别是>>> 参数一 参数二 参数三
    # 子进程执行完毕!
    # 主进程执行完毕!
    #怎么样开启多个进程呢?for循环。而且我有个需求就是说,全部的子进程异步执行,而后全部的子进程所有执行完以后,我再执行主进程,怎么搞?看代码
    def func(x,y):
        print(x)
        # time.sleep(1) #进程切换:若是没有这个时间间隔,那么你会发现func执行结果是打印一个x而后一个y,再打印一个x一个y,不会出现打印多个x而后打印y的状况,由于两个打印距离太近了并且执行的也很是快,可是若是你这段程序运行慢的话,你就会发现进程之间的切换了。
        print(y)
    if __name__ == '__main__':
        p_list= []
        for i in range(10):
            p = Process(target=func,args=('姑娘%s'%i,'来玩啊!'))
            p_list.append(p)
            p.start()
        [ap.join() for ap in p_list] #四、这是解决办法,前提是咱们的子进程所有都已经去执行了,那么我在一次给全部正在执行的子进程加上join,那么主进程就须要等着全部子进程执行结束才会继续执行本身的程序了,而且保障了全部子进程是异步执行的。
           # p.join()放在for内: 若是加到for循环里面,那么全部子进程包括父进程就所有变为同步了,由于for循环也是主进程的,循环第一次的时候,一个进程去执行了,而后这个进程就join住了,那么for循环就不会继续执行了,等着第一个子进程执行结束才会继续执行for循环去建立第二个子进程。
            #二、若是我不想这样的,也就是我想全部的子进程是异步的,而后全部的子进程执行完了再执行主进程
        #p.join()在for外部:若是这样写的话,屡次运行以后,你会发现会出现主进程的程序比一些子进程先执行完,由于咱们p.join()是对最后一个子进程进行了join,也就是说若是这最后一个子进程先于其余子进程执行完,那么主进程就会去执行,而此时若是还有一些子进程没有执行完,而主进程执行完了,那么就会先打印主进程的内容了,这个cpu调度进程的机制有关系,由于咱们的电脑可能只有4个cpu,个人子进程加上住进程有11个,虽然我for循环是按顺序起进程的,可是操做系统必定会按照顺序给你执行你的进程吗,答案是不会的,操做系统会按照本身的算法来分配进程给cpu去执行,这里也解释了咱们打印出来的子进程中的内容也是没有固定顺序的缘由,由于打印结果也须要调用cpu,能够理解成进程在争抢cpu,若是同窗你想问这是什么算法,这就要去研究操做系统啦。那咱们的想全部子进程异步执行,而后再执行主进程的这个需求怎么解决啊
        print('不要钱~~~~~~~~~~~~~~~~!')
        
    #列:一、同时对一个文件进行写操做 二、同时建立多个文件
    import time
    import os
    import re
    from multiprocessing import Process
    #多进程同时对一个文件进行写操做
    def func(x,y,i):
        with open(x,'a',encoding='utf-8') as f:
            print('当前进程%s拿到的文件的光标位置>>%s'%(os.getpid(),f.tell()))
            f.write(y)
    #多进程同时建立多个文件
    # def func(x, y):
    #     with open(x, 'w', encoding='utf-8') as f:
    #         f.write(y)
    if __name__ == '__main__':
        p_list= []
        for i in range(10):
            p = Process(target=func,args=('can_do_girl_lists.txt','姑娘%s'%i,i)) 
            # p = Process(target=func,args=('can_do_girl_info%s.txt'%i,'姑娘电话0000%s'%i))
            p_list.append(p)
            p.start()
        [ap.join() for ap in p_list] #这就是个for循环,只不过用列表生成式的形式写的
        with open('can_do_girl_lists.txt','r',encoding='utf-8') as f:
            data = f.read()
            all_num = re.findall('\d+',data) #打开文件,统计一下里面有多少个数据,每一个数据都有个数字,因此re匹配一下就好了
            print('>>>>>',all_num,'.....%s'%(len(all_num)))
        #print([i in in os.walk(r'你的文件夹路径')])
        print('不要钱~~~~~~~~~~~~~~~~!')
      
    #例2terminate关闭子进程
    def func():
        print("子进程开始执行!")
        time.sleep(2)
        print("子进程执行完毕!")
    if __name__ == '__main__':
        p = Process(target=func,)
        p.start()
        p.terminate()  # 给操做系统发送一个关闭进程p1的信号,让操做系统去关闭它
        time.sleep(1)
        """因为操做系统关闭子进程的过程须要作许多事情(如回收资源),这是须要耗费必定时间的,
       若是在给操做系统发出关闭信号后(p1.terminate())马上判断子进程是否还活着,结果是不许
       确的,此时操做系统正在关闭子进程,因此咱们要等待必定时间才能够获得正确的判断结果."""
        print("子进程是否还活着>>>", p.is_alive())
        print("主进程执行完毕!")
        
     #例3进程对象的其余方法一:terminate,is_alive
    class Piao(Process):
        def __init__(self,name):
            self.name=name
            super().__init__()
        def run(self):
            print('%s is 打飞机' %self.name)
            # s = input('???') #别忘了再pycharm下子进程中不能input输入,会报错EOFError: EOF when reading a line,由于子进程中没有像咱们主进程这样的在pycharm下的控制台能够输入东西的地方
            time.sleep(2)
            print('%s is 打飞机结束' %self.name)
    if __name__ == '__main__':
        p1=Piao('太白')
        p1.start()
        time.sleep(5)
        p1.terminate()#关闭进程,不会当即关闭,有个等着操做系统去关闭这个进程的时间,因此is_alive马上查看的结果可能仍是存活,可是稍微等一会,就被关掉了
        print(p1.is_alive()) #结果为True
        print('等会。。。。')
        time.sleep(1)
        print(p1.is_alive()) #结果为False
            
     #例4Process类中自带的self.name 在父类的init函数在已存在
    class Piao(Process):
        def __init__(self,name):
            self.name=name
            super().__init__() #Process的__init__方法会执行self.name=Piao-1,覆盖egon
                               #因此加到这里,会覆盖咱们的self.name=name
            #为咱们开启的进程设置名字的作法
            # super().__init__() # 这种状况不会影响咱们的名字,不会被覆盖
            # self.name=name
        def run(self):
            print('%s is piaoing' %self.name)  
            time.sleep(random.randrange(1,3))
            print('%s is piao end' %self.name)
    if __name__=='__main__':
        p=Piao('egon')
        p.start()
        print('开始')
        print(p.pid) #查看pid
    #开始
    #15148
    #Piao-1 is piaoing
    #Piao-1 is piao end
    #例5,子进程的import过程
    from multiprocessing import Process
    import time
    def func():
        print('ok')
    print(1)  #1 第一次打印主程序 打印 1
    if __name__=='__main__':
        p=Process(target=func) #2 开始导入主模块,import 因此还会执行 打印 1,3
        p.start()  #3 打印 ok
        a=2
        time.sleep(2)
        print('a',a)  #4打印 a ,2
    #若是外部调用a
    # print(a) # 报错,子进程没有as
    print('3')  #5 打印3
     
  • 在windows中Process()必须放到# if name == 'main':下安全

     
     
     
    xxxxxxxxxx
     
     
     
     
    因为Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。 
    若是在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)无限import。 
    这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。
     
  • 进程建立的第二种方法:(继承)

     
     
     
    xxxxxxxxxx
     
     
     
     
    class MyProcess(Process): #本身写一个类,继承Process类
        #咱们经过init方法能够传参数,若是只写一个run方法,那么无法传参数,由于建立对象的是传参就是在init方法里面,面向对象的时候,咱们是否是学过
        def __init__(self,person):
            super().__init__()
            self.person=person
        def run(self):
            print(os.getpid())
            print(self.pid)
            print(self.pid)
            print('%s 正在聊天' %self.person)
            
        # def start(self):
        #     #若是你非要写一个start方法,能够这样写,而且在run方法先后,能够写一些其余的逻辑
        #     self.run()
    if __name__ == '__main__':
        p1=MyProcess('Jedan')
        p2=MyProcess('太白')
        p3=MyProcess('alexDSB')
        p1.start() #start内部会自动调用run方法,这是在写了start方法后
        p2.start() 
        # p2.run()
        p3.start()
        p1.join()
        p2.join()
        p3.join()
     
  • 进程的内存空间是隔离的网络

     
     
     
    xxxxxxxxxx
     
     
     
     
    import time
    from multiprocessing import Process
    global_num = 100
    def func():    # 子进程
        global global_num
        global_num = 0
        print("子进程的全局变量>>>", global_num)
    if __name__ == '__main__':
        p1 = Process(target=func,)
        p1.start()
        time.sleep(1)  # 等待子进程执行结束
        print("主进程的全局变量>>>", global_num)
    # 执行结果:
    # 子进程的全局变量>>> 0
    # 主进程的全局变量>>> 100
    # 得出结论:
    # 进程之间是空间隔离的,不共享资源
     
  • 子进程中不能使用input:若是在子进程中存在input,因为输入台只显示在主进程中,子进程是没有输入台的,因而系统会自动报错.因此不要在子进程中出现input。数据结构

  • 僵尸进程和孤儿进程:

    • 僵尸进程(有害):任何一个子进程(init除外)在exit()以后,并不是立刻就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每一个子进程在结束时都要通过的阶段。若是子进程在exit()以后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。若是父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不通过僵尸状态。 若是父进程在子进程结束以前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
    • 孤儿进程(无害):一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工做。孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工做。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会表明党和政府出面处理它的一切善后工做。所以孤儿进程并不会有什么危害。
  • 守护进程

    • 使用日常的方法时,子进程是不会随着主进程的结束而结束,只有当主进程和子进程所有执行完毕后,程序才会结束.可是,若是咱们的需求是: 主进程执行结束,由该主进程建立的子进程必须跟着结束. 这时,咱们就须要用到守护进程了

    • 主进程建立守护进程:

      • 其一: 守护进程会在主进程代码执行结束后就终止
      • 其二: 守护进程内没法再开启子进程,不然抛出异常: AssertionError: daemonic processes are not allowed to have children
      • 须要注意的是: 进程之间是相互独立的,主进程代码运行结束,守护进程随机终止
     
     
     
    xxxxxxxxxx
     
     
     
     
    import os
    import time
    from multiprocessing import Process
    class Myprocess(Process):
        def __init__(self,person):
            super().__init__()
            self.person = person  
        def run(self):
            print("这我的的ID号是:%s" % os.getpid())
            print("这我的的名字是:%s" % self.name)
            time.sleep(3)
    if __name__ == '__main__':
        p=Myprocess('李华')
        p.daemon=True #必定要在p.start()前设置,设置p为守护进程,禁止p建立子进程,而且父进程代码执行结束,p即终止运行
        p.start()
        # time.sleep(1) # 在sleep时linux下查看进程id对应的进程ps -ef|grep id
        print('主进程执行完毕!')   #这种状况子进程都还没进行就结束了,就打印'主程序……'
     

总结:

一、主进程和子进程互不干扰 二、主进程执行完毕以后程序不会结束,会等待全部的子进程结束以后才结束 三、为何主进程要等待子进程结束以后才结束? 由于主进程要负责给子进程回收一些系统资源 四、守护进程 : 是一个子进程,守护的是主进程 结束条件 : 主进程的代码结束,守护进程也结束 五、进程 主进程的代码结束,守护进程结束 主进程要回收守护进程(子进程)的资源 主进程等待其余全部子进程结束 主进程回收全部子进程的资源

 

使用原生socket模块写一个实现多人聊天的功能?

#服务端
from socket import *
from multiprocessing import Process
def talk(conn,client_addr):
    while True:
        try:
            msg=conn.recv(1024)
            print('客户端消息>>',msg)
            if not msg:break
            conn.send(msg.upper())
            #在这里有同窗可能会想,我能不能在这里写input来本身输入内容和客户端进行对话?朋友,是这样的,按说是能够的,可是须要什么呢?须要你像咱们用pycharm的是同样下面有一个输入内容的控制台,当咱们的子进程去执行的时候,咱们是没有地方能够显示可以让你输入内容的控制台的,因此你没办法输入,就会给你报错。
        except Exception:
            break

if __name__ == '__main__': #windows下start进程必定要写到这下面
    server = socket(AF_INET, SOCK_STREAM)
    # server.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)  # 若是你将若是你将bind这些代码写到if __name__ == '__main__'这行代码的上面,那么地址重用必需要有,由于咱们知道windows建立的子进程是对整个当前文件的内容进行的copy,前面说了就像import,若是你开启了子进程,那么子进程是会执行bind的,那么你的主进程bind了这个ip和端口,子进程在进行bind的时候就会报错。
    server.bind(('127.0.0.1', 8080))
    #有同窗可能还会想,我为何多个进程就能够链接一个server段的一个ip和端口了呢,我记得当时说tcp的socket的时候,我是不能在你这个ip和端口被链接的状况下再链接你的啊,这里是由于当时咱们就是一个进程,一个进程里面是只能一个链接的,多进程是能够多链接的,这和进程之间是单独的内存空间有关系,先这样记住他,好吗?
    server.listen(5)
    while True:
        conn,client_addr=server.accept()
        p=Process(target=talk,args=(conn,client_addr))
        p.start()

#客服端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if not msg:continue

    client.send(msg.encode('utf-8'))
    msg=client.recv(1024)
    print(msg.decode('utf-8'))
相关文章
相关标签/搜索