zoukankan      html  css  js  c++  java
  • 并编程复习——协程,池

    进程池和线程池

    1、池

    '''
    池:为了减缓计算机硬件的压力,避免计算机的硬件的设备崩溃
    虽然减轻了计算机硬件的压力,但是一定程度上降低了持续的效率
    	
    进程池线程池:
        为了限制开设的进程数和线程数,从而保证计算机硬件的安全
    '''
    
    • 如何使用池
    #1 介绍
    concurrent.futures模块提供了高度封装的异步调用接口
    ThreadPoolExecutor:线程池,提供异步调用
    ProcessPoolExecutor: 进程池,提供异步调用
    Both implement the same interface, which is defined by the abstract Executor class.
    
    #2 基本方法
    #submit(fn, *args, **kwargs)
    异步提交任务
    
    #map(func, *iterables, timeout=None, chunksize=1) 
    取代for循环submit的操作
    
    #shutdown(wait=True) 
    相当于进程池的pool.close()+pool.join()操作
    wait=True,等待池内所有任务执行完毕回收完资源后才继续
    wait=False,立即返回,并不会等待池内的任务执行完毕
    但不管wait参数为何值,整个程序都会等到所有任务执行完毕
    submit和map必须在shutdown之前
    
    #result(timeout=None)
    取得结果
    
    #add_done_callback(fn)
    回调函数
    
    • 案例
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import time
    import os
    
    
    
    # 示例化池对象
    # 不知道参数的情况,默认是当前计算机cpu个数乘以5,也可以指定线程个数
    pool = ProcessPoolExecutor(5)  # 创建了一个池子,池子里面有20个线程
    
    
    def task(n):
        print(n,os.getpid())
        time.sleep(2)
        return n**2
    
    
    def call_back(n):
        print('我拿到了结果:%s'%n.result())
    """
    提交任务的方式
        同步:提交任务之后,原地等待任务的返回结果,再继续执行下一步代码
        异步:提交任务之后,不等待任务的返回结果(通过回调函数拿到返回结果并处理),直接执行下一步操作
    """
    
    
    # 回调函数:异步提交之后一旦任务有返回结果,自动交给另外一个去执行
    if __name__ == '__main__':
        # pool.submit(task,1)
        t_list = []
        for i in range(20):
            future = pool.submit(task,i).add_done_callback(call_back)  # 异步提交任务
            t_list.append(future)
    
        # pool.shutdown()  # 关闭池子并且等待池子中所有的任务运行完毕
        # for p in t_list:
        #     print('>>>:',p.result())
        print('主')
    
    

    协程

    • 进程:资源单位
    • 协程:执行单位
    • 协程:单线程下实现并发(能够在多个任务之间切换和保存状态来节省IO)

    注:协程是程序员想象出来的,对操作系统来说是不存在的

    将单个线程的效率提升到最高,多进程下开多线程,多线程下用协程>>> 实现高并发!!!

    # yield能够实现保存上次运行状态,但是无法识别遇到io才切换
    
    #串行执行
    import time
    
    def func1():
        for i in range(10000000):
            i+1
    
    def func2():
        for i in range(10000000):
            i+1
    
    start = time.time()
    func1()
    func2()
    stop = time.time()
    print(stop - start)
    
    
    #基于yield并发执行
    import time
    def func1():
        while True:
            10000000+1
            yield
    
    def func2():
        g=func1()
        for i in range(10000000):
            # time.sleep(100)  # 模拟IO,yield并不会捕捉到并自动切换
            i+1
            next(g)
    
    start=time.time()
    func2()
    stop=time.time()
    print(stop-start)
    
    • gevent 模块

      一个spawn就是一个帮你管理任务的对象

      gevent模块不能识别它本身以外的所有的IO行为,但是它内部封装了一个模块,能够帮助我们识别所有的IO行为

    • 例子

    from gevent import monkey;monkey.patch_all()  # 检测所有的IO行为
    from gevent import spawn,joinall  # joinall列表里面放多个对象,实现join效果
    import time
    
    def play(name):
        print('%s play 1' %name)
        time.sleep(5)
        print('%s play 2' %name)
    
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(3)
        print('%s eat 2' %name)
    
    
    start=time.time()
    g1=spawn(play,'刘清正')
    g2=spawn(eat,'刘清正')
    
    # g1.join()
    # g2.join()
    joinall([g1,g2])
    print('主',time.time()-start)  # 单线程下实现并发,提升效率
    
    • 例:协程实现服务端与客户端的通信
    # 服务端
    from gevent import monkey;monkey.patch_all()
    from socket import *
    from gevent import spawn
    
    
    def communicate(conn):
        while True:
            try:
                data = conn.recv(1024)
                if len(data) == 0: break
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
        
    
    def server(ip, port, backlog=5):
        server = socket(AF_INET, SOCK_STREAM)
        server.bind((ip, port))
        server.listen(backlog)
    
        while True:  # 链接循环
            conn, client_addr = server.accept()
            print(client_addr)
    
            # 通信
            spawn(comunicate,conn)
    
    
    if __name__ == '__main__':
        g1=spawn(server,'127.0.0.1',8080)
        g1.join()
    
        
    # 客户端
    from threading import Thread, current_thread
    from socket import *
    
    
    def client():
        client = socket(AF_INET, SOCK_STREAM)
        client.connect(('127.0.0.1', 8080))
    
        n = 0
        while True:
            msg = '%s say hello %s' % (current_thread().name, n)
            n += 1
            client.send(msg.encode('utf-8'))
            data = client.recv(1024)
            print(data.decode('utf-8'))
    
    
    if __name__ == '__main__':
        for i in range(500):
            t = Thread(target=client)
            t.start()
    # 原本服务端需要开启500个线程才能跟500个客户端通信,现在只需要一个线程就可以扛住500客户端
    # 进程下面开多个线程,线程下面再开多个协程,最大化提升软件运行效率
    
  • 相关阅读:
    [opentwebst]一个简单的登陆脚本
    opentwebst一个ie自动化操作测试软件-功能强大
    给X9DRL-iF双路服务器主板刷BIOS
    在ubuntu16下面通过kvm+lvm安装ubuntu16的虚拟机
    ubuntu16安装KVM
    PowerShell全自动分配CPU
    在ubuntu16编译安装nginx-1.10.2(full)完全自带组件
    将博客搬至CSDN
    乌邦图ubuntu配置iptables的NAT上网
    LVM增大和减小ext4、xfs分区
  • 原文地址:https://www.cnblogs.com/king-home/p/10896534.html
Copyright © 2011-2022 走看看