zoukankan      html  css  js  c++  java
  • 洗礼灵魂,修炼python(89)-- 知识拾遗篇 —— 进程

    进程

    1.含义:计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。说白了就是一个程序的执行实例。

    执行一个程序就是一个进程,比如你打开浏览器看到我的博客,浏览器本身是一个软件程序,你此时打开的浏览器就是一个进程。

    2.进程的特性

    • 一个进程里可以有多个子进程

    • 新的进程的创建是完全拷贝整个主进程

    • 进程里可以包含线程

    • 进程之间(包括主进程和子进程)不存在数据共享,相互通信(浏览器和python之间的数据不能互通的),要通信则要借助队列,管道之类的

    3.进程和线程之间的区别

    • 线程共享地址空间,而进程之间有相互独立的空间

    • 线程之间数据互通,相互操作,而进程不可以

    • 新的线程比新的进程创建简单,比开进程的开销小很多

    • 主线程可以影响子线程,而主进程不能影响子进程

     

    4.在python中,进程与线程的用法就只是名字不同,使用的方法也是没多大区别

    5.简单实例

    1)创建一个简单的多进程:

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing,time
    
    def func(name):
        time.sleep(1)
        print('hello',name,time.ctime())
    
    ml = []
    for i in range(3):
        p = multiprocessing.Process(target=func,args=('yang',))
        p.start()
        ml.append(p)
    
    for i in ml:
        i.join() #注意这里,进程必须加join方法,不然会导致僵尸进程
    

      

    运行结果:

    不管怎么说,反正报错了,同样的代码,在python自带的IDLE里试试:

    没有任何东西就结束了。好的,这里要说下了,按照我个人的理解,当你用pycharm或者IDLE时,pycharm或者IDLE在你的电脑里本身也是一个进程,并且默认是主进程。所以在pycharm会报错,而在IDLE里运行就是空白,个人理解,对不对暂且不谈,后期学到子进程时再说。

    解决办法就是,其他的不变,加一个if __name == '__main__'判断就行:

    这样就解决了,好的,你现在可以体会到那句话了,进程与线程的用法就只是名字不同,使用的方法也是没多大区别。不多说,自行体会。而运行结果看到的时间是同步的,那么这进程才是真正意义上的并行运行。

    2)自定义类式进程

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing,time
    
    class myprocess(multiprocessing.Process):
        def __init__(self,name):
            super(myprocess,self).__init__()
            self.name = name
    
        def run(self):
            time.sleep(1)
            print('hello',self.name,time.ctime())
    
    if __name__ == '__main__':
        ml = []
        for i in range(3):
            p = myprocess('yang')
            p.start()
            ml.append(p)
    
        for j in ml:
            j.join()
    

      

    运行结果:

    然后setDaemon之类的方法和线程也是完全一致的。

    3)每一个进程都有根进程,换句话,每一个进程都有父进程

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing,time,os
    
    def info():
        print('mudule name:',__name__)
        print('parent process:',os.getppid()) #父进程号
        print('son process:',os.getpid())     #子进程号
    
    if __name__ == '__main__':
        info()
        print('-----')
        p = multiprocessing.Process(target=info,args=[])
        p.start()
        p.join()
    

      

    运行结果:

    而查看我本机的进程:

    可以知道,6204就是pycharm,正是此时的根进程,而主进程就是我这个py文件(由__main__可知),接着再往下的子进程等等等的。

    6.多进程间的通信和数据共享

    首先我们都已经知道进程之间是独立的,不可以互通,并且数据相互独立,而在实际开发中,一定会遇到需要进程间通信的场景要求,那么我们怎么搞呢

    有两种方法:

    • pipe
    • queue

    1)使用queue通信

    在多线程那里已经学过queue了,创建queue的方式,q = queue.Queue(),这种创建是创建的线程queue,并不是进程queue。创建进程queue的方式是:

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing
    
    def func(q,name,age): #这里必须要把q对象作为参数传入才能实现进程之间通信
        q.put({'name':name,'age':age})
    
    if __name__ == '__main__':
        q = multiprocessing.Queue() #创建进程queue对象
        ml = []
        for i in range(3):
            p = multiprocessing.Process(target=func,args=(q,'yang',21))
            p.start()
            ml.append(p)
        print(q.get()) #获取queue信息
        print(q.get()) 
        print(q.get())
        for i in ml:
            i.join()
    

      

    运行结果:

    好的,已经通过queue实现通信,那么细心的朋友可能会想,此时的queue到底是同一个呢还是copy的呢?开始测试,码如下:

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing
    
    def func(q,name,age):
        q.put({'name':name,'age':age})
        print('id:',id(q))
    if __name__ == '__main__':
        q = multiprocessing.Queue()
        ml = []
        print('id:',id(q))
        for i in range(3):
            p = multiprocessing.Process(target=func,args=(q,'yang',21))
            p.start()
            ml.append(p)
        print(q.get())
        print(q.get())
        print(q.get())
        for i in ml:
            i.join()
    

      

    在Windows平台运行结果:

    Linux的ubuntu下是这样的:

    这就不好怎么说了,我个人的理解,线程和进程这类与电脑硬件(CPU,RAM)等有联系的都有不确定因素,姑且认为在Windows平台里queue是copy的,在Linux里是同一个吧,并且据经验人士表示,在macbook上也是同一个。

    还有个问题, 假如使用的queue是线程式的呢?

    代码其他都没变,只改了这里:

    结果:

    虽然报错了,但是却有一个关键点,提示的是不能pickle线程锁对象,也就是说刚才我们使用的queue是进程对象,所以可以pickle,注意了,这里就是关键点,使用了pickle,那么也就是说,在Windows平台里是copy的,如果不是copy,就不需要存在pickle对吧?直接拿来用就是啊,干嘛要pickle之后取的时候再反pickle呢对吧?

    再看Linux下呢,由于Linux默认是python2,所以模块包名稍微有点不同

    结果阻塞住了,但是前面的还是出来了,看到的id果然还是一样的。

    这里就有三点需要注意:(个人理解,如有误望指正)

    1.进程里的确不能使用线程式queue

    2.Windows平台的进程式queue是copy的

    3.Linux平台的线程式和进程式都是同一个,但是如果在进程里使用线程式queue会阻塞住

    但我个人觉得copy更有安全性

    2)使用pipe通信

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing
    
    def func(conn):
        conn.send('约吗?')  #子进程发送数据
        print(conn.recv())  #接受数据,不能加参数1024之类的
        conn.close()        #子进程关闭连接
    if __name__ == '__main__':
        parent_conn,son_conn = multiprocessing.Pipe() #创建pipe对象,父进程,子进程
        ml = []
        p = multiprocessing.Process(target=func,args=(son_conn,))
        p.start()
        print(parent_conn.recv())  #父进程接受数据,不能加参数1024之类的
        parent_conn.send('不约')    #发送数据
        p.join()                   #join方法是进程特有
    

      

    运行结果:

    这样就联系上了,相信你发现了,基本和前面的socket差不多,不过唯一的不同是recv()方法不能加参数,不信的话,你加来试试

    反观线程通信,相信你会觉得进程比线程更方便

    当然pipe也可以有多个:

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing,time
    
    def func(conn):
        conn.send('约吗?')  #子进程发送数据
        print(conn.recv())
        conn.close()        #子进程关闭连接
    if __name__ == '__main__':
        parent_conn,son_conn = multiprocessing.Pipe() #创建pipe对象,父进程,子进程
        ml = []
        for i in range(3):
            p = multiprocessing.Process(target=func,args=(son_conn,))
            p.start()
            ml.append(p)
            print(parent_conn.recv())  #父进程接受数据,不能加参数1024之类的
            parent_conn.send('不约')
        for i in ml:
            i.join()
    

      

    运行结果:

    7.进程之间数据共享——manager

    比较简单,就利用了进程里的manager对象下的各个数据类型,其他的很简单的,我就不注释了

    #!usr/bin/env python
    #-*- coding:utf-8 -*-
    
    # author:yangva
    
    import multiprocessing
    
    def func(l,d,num):
        l.append(num)
        d[num] = num
    
    if __name__ == '__main__':
        with multiprocessing.Manager() as manager:
            l = manager.list()
            d = manager.dict()
            ml = []
            for i in range(6):
                p = multiprocessing.Process(target=func,args=(l,d,i))
                p.start()
                ml.append(p)
            for i in ml:
                i.join()
            print('d:',d)
            print('l:',l)
    

      

    运行结果:

    这样是不是就实现了数据共享了?

    好的,进程也解析完了

  • 相关阅读:
    Android Preference 实现长按监听 longclickable
    表达式求值
    二分图大讲堂——彻底搞定最大匹配数(最小覆盖数)、最大独立数、最小路径覆盖、带权最优匹配
    poj 1806
    树的公共祖先问题LCA
    给定两个长度相同,分别有序的数组A和B,求两个数组中所有数的中位数
    网络爬虫基本原理(转载)
    atoi函数的实现
    一个天平,12个大小,外观相同的球,一个球的重量与其他的不同,称3次找出问题小球
    动态规划求RMQ(区间最值问题Range Minimum/Maximum Query)
  • 原文地址:https://www.cnblogs.com/Eeyhan/p/8449549.html
Copyright © 2011-2022 走看看