美文网首页IT在线课程Python 运维程序员
Python篇-多进程与协程的理解与使用

Python篇-多进程与协程的理解与使用

作者: TianTianBaby223 | 来源:发表于2017-11-25 15:54 被阅读725次

    TZ : 朴实的劳作也能硕果累累

    一 : 科普一分钟

    • 尽管进程间是独立存在的,不能相互访问彼此的数据,但是在python中却存在进程间的通信方法,来帮助我们可以利用多核CPU也能共享数据.
    • 对于多线程其实也是存在一些缺点的,不是任何场景我们都用多线程来完成并发处理任务,因为CPU操作线程,所以线程多了,对于计算机的资源消耗是十分严重的,多线程适合IO操作密集的任务,那么怎么办呢, 协程的出现帮我们解决了这个问题 ,协程是比线程更小的一个单位,但是它的作用却不容忽视.

    二 : 多进程

    1. 多进程简单了解 :

    进程之间是独立的,是操作系统自己来维护和管理的,python通过C接口起了一个进程,多进程可以充分的利用多核CPU

    2. 多进程的创建 :
    import  multiprocessing
    import time
    
    # 子进程执行方法
    def run(person):
        time.sleep(2)
        print(person)
    
    #循环创建 10个子进程
    for i in  range(10):
    #创建子进程实例
        p = multiprocessing.Process(target=run,args=('雪芙 %s' %i ,))
    #运行子进程
        p.start()
    
    3. 多进程间的通信 :

    进程间独立,如果想相互访问,就必须有一个中间翻译,下面提供了几种进程间通信的方法

    • 进程Queue
    from  multiprocessing import  Process,Queue
    
    # import  queue
    
    #子进程,内存独立,相当于数据的传递
    def f(subQueue):
        subQueue.put("雪芙")
    
    if __name__ == '__main__':
        #进程Queue
        q = Queue()
        #创建进程
        p = Process(target=f,args=(q,))
        #执行进程
        p.start()
        print(q.get())
    

    解析 :
    Queue通信,相当于父进程赋值了一个Queue给子进程,子进程在这个Queue放好数据后,序列化一个中间翻译,然后在反序列化返回给父进程,
    因为进程之间内存独立,不能传递对象传递的其实就是序列化的数据

    • Pipe
      多进程还有一种数据传递方式叫管道原理和 Queue相同
    # Author:TianTianBaby
    
    from  multiprocessing import Process,Pipe
    
    #子进程执行方法
    def f(Subconn):
        Subconn.send("吃了吗")
        print("来自父亲的问候 : ",Subconn.recv())
        Subconn.close()
    
    
    if __name__ == '__main__':
    #创建管道两端
        parent_conn,child_conn = Pipe()
    #创建子进程
        p = Process(target=f,args=(child_conn,))
        p.start()
        print("来自儿子的问候 :",parent_conn.recv())
        parent_conn.send("你好啊")
        p.join()
    
    4. 进程锁

    虽然内存独立,但是即使是打印也会造成打印数据错误,为了防止进程间抢屏幕打印输出,加了进程锁

    from multiprocessing import  Process,Lock
    
    #子进程执行方法
    def f(lock,num):
        lock.acquire()
        print("tztztz",num)
        lock.release()
    
    if __name__ == '__main__':
        lock = Lock()
    #循环创建100个子进程
        for num in range(100):
            Process(target=f,args=(lock,num)).start()
    
    5. 进程池

    创建一个子进程相当于copy一份父进程内存数据,为了防止频繁创建,导致内存不足,所以有了进程池作为限制.

    # Author:TianTianBaby
    
    from multiprocessing import Process,Pool
    
    import  time,os
    
    def  son(i):
        time.sleep(2)
        return  i+100
    
    def Back(arg):
        print('你好完成')
    
    
    #允许进程池同时放入5个进程
    pool = Pool(processes=3)
    
    for i in range(10):
        #并行  callback 回调(主进程调用的)
        pool.apply_async(func=son,args=(i,),callback=Back)
       #并行
        # pool.apply_async(func=son, args=(i,))
        #串行
        # pool.apply(func=son,args=(i,))
    
    print('end')
    #先关闭进程池再join
    pool.close()
    #进程池中进程执行完毕再关闭,如果注释,那么程序直接关闭
    pool.join()
    

    三 : 协程

    1. 协程的简单了解 :

    协程又称微线程,coroutne,协程是一种用户态的轻量级线程

    通俗点讲就是周末我在家里休息,假如我先洗漱,再煮饭,再下载电影看会很慢,用了协程的效果就好比,我在下载电影的时候去点火煮饭,此时我马上洗漱,等我洗漱好了,饭也好了,吃完饭了,电影下好了,我可以看了.

    讲究效率的协程
    2. 协程的创建和使用 :

    gevent 是一个三方库,可以轻松通过gevent实现并发同步或者异步编程.

    # Author:TianTianBaby
    
    import  gevent
    #函数1
    def first():
        print('运行 1')
        gevent.sleep(2)
        print('回到1')#精确的文本内容切换到 ..
    
    #函数2
    def second():
        print('运行2')
        gevent.sleep(1)
        print('回到2')
    
    #函数3
    def third():
        print("运行3")
        gevent.sleep(0)
        print("回到3")
    
    #创建并添加写成任务
    gevent.joinall([gevent.spawn(first), #生成
                    gevent.spawn(second),
                    gevent.spawn(third)])
    

    解析:尝试运行发现,运行时间为Sleep最长的时间,也就是说协程能绕过IO,进行执行,极大的提高了效率.
    IO(从硬盘上读一块数据,从网络读数据,从内存里读一块数据) 操作不占用CPU,计算占用CPU

    3. 协程简单爬网页 :
    # Author:TianTianBaby
    from urllib import  request
    import  ssl
    import gevent
    from  gevent import  monkey
    
    #把当前程序的所有的IO操作 单独做上标记
    monkey.patch_all()
    ssl._create_default_https_context = ssl._create_unverified_context
    def f(url):
        print('GET:%s' %url)
        resp = request.urlopen(url)
        print(resp)
        data = resp.read()
        f = open("pa.html","wb")
        f.write(data)
        f.close()
        print("%d bytes received from %s" %(len(data),url))
    
    gevent.joinall([gevent.spawn(f,'http://www.jianshu.com/'),
                    gevent.spawn(f,'http://www.iconfont.cn/'),
                    gevent.spawn(f,'http://www.cocoachina.com/'),
                    ])
    
    4. 协程实现socketServer:

    通过协程,我们可以写出一个socketServer,真正socketServer的底层是用多线程来实现,我们用写成写出的效率很高,而且非常节省内存

    
    import  sys
    import  socket
    import time
    import gevent
    
    from gevent import socket,monkey
    monkey.patch_all()
    
    def server(port):
        s = socket.socket()
        s.bind(('localhost',port))
        s.listen(500)
        while True:
            cli,addr = s.accept()
            #交给协程处理
            gevent.spawn(handle_request,cli)
    
    def handle_request(conn):
        try:
            while True:
                data = conn.recv(1024)
                print("recv:",data)
                conn.send(data)
                if not data:
                    conn.shutdown(socket.SHUT_WR)
    
        except  Exception as  ex:
            print(ex)
    
        finally:
            conn.close()
    
    if __name__ == '__main__':
        server(9001)
    

    四 : 总结

    • 协程的优点
      1 : 线程在单线程下切换,减少资源消耗
      2 : 无需原子操作控制流,简化编程模型
      3 : 高并发,高扩展,低成本.

    • 无论是多进程,多线程还是协程在不同的场景用不同的模型才能高效的完成任务.

    相关文章

      网友评论

        本文标题:Python篇-多进程与协程的理解与使用

        本文链接:https://www.haomeiwen.com/subject/dyiqbxtx.html