• Python(进程池与协程)


    1.进程池与线程池:
    为什么要用“池”:池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务
    池子内什么时候装进程:并发的任务属于计算密集型
    池子内什么时候装线程:并发的任务属于IO密集型
    
    1、进程池
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,os,random
    
    def task(x):
        print('%s 接客' %os.getpid())
        time.sleep(random.randint(2,5))
        return x**2
    
    if __name__ == '__main__':
        p=ProcessPoolExecutor() # 默认开启的进程数是cpu的核数
    
        # alex,武佩奇,杨里,吴晨芋,张三
    
        for i in range(20):
            p.submit(task,i)
    
    2、线程池
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,os,random
    
    def task(x):
        print('%s 接客' %x)
        time.sleep(random.randint(2,5))
        return x**2
    
    if __name__ == '__main__':
        p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
    
        # alex,武佩奇,杨里,吴晨芋,张三
    
        for i in range(20):
            p.submit(task,i)
    2.同步、异步、阻塞、非阻塞
    1、阻塞与非阻塞指的是程序的两种运行状态
    阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源
    非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地,执行其他操作,力求尽可能多的占有CPU
    
    2、同步与异步指的是提交任务的两种方式:
    同步调用:提交完任务后,就在原地等待,直到任务运行完毕后,拿到任务的返回值,才继续执行下一行代码
    异步调用:提交完任务后,不在原地等待,直接执行下一行代码。等全部执行完毕后取出结果
    
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,os,random
    
    def task(x):
        print('%s 接客' %x)
        time.sleep(random.randint(1,3))
        return x**2
    
    if __name__ == '__main__':
        # 异步调用
        p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
    
        # alex,武佩奇,杨里,吴晨芋,张三
    
        obj_l=[]
        for i in range(10):
            obj=p.submit(task,i)
            obj_l.append(obj)
    
        # p.close()
        # p.join()
        p.shutdown(wait=True) (等同于p.close()(不允许向池中放新任务) + p.join())
    
        print(obj_l[3].result())
        print(''# 同步调用
        p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
    
        # alex,武佩奇,杨里,吴晨芋,张三
    
        for i in range(10):
            res=p.submit(task,i).result()
    
    print('')
    2.协程
    1、单线程下实现并发:协程 (为了提高效率;但不是说所有协程都会提升效率)
       并发指的多个任务看起来是同时运行的;并发实现的本质:切换+保存状态
       有效的协程在一定程度‘骗过’了CPU;通过自己内部协调,一遇到IO就切到自己的其他程序中,使得CPU以为这个程序一直在运行,从而使其更有可能处于就绪态或运行态,以更多的占用CPU。
    2、实现并发的三种手段:
    a)单线程下的并发;由程序自己控制,相对速度快
    b)多线程下的并发;由操作系统控制,相对速度较慢
    c)多进程下的并发;由操作系统控制,相对速度慢
    
    3、基于yield保存状态,实现两个任务直接来回切换,即并发的效果 (但yield不会遇到阻塞自动切程序)
       PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
    
    import time
    def consumer():
        '''任务1:接收数据,处理数据'''
        while True:
            x=yield
    
    def producer():
        '''任务2:生产数据'''
        g=consumer()
        next(g)
        for i in range(10000000):
            g.send(i)
    
    start=time.time()
    producer() #1.0202116966247559
    stop=time.time()
    print(stop-start)
    
    # 纯计算的任务并发执行
    import time
    def task1():
        res=1
        for i in range(1000000):
            res+=i
            yield
            time.sleep(10000)  #yield不会自动跳过阻塞
            print('task1')
    
    def task2():
        g=task1()
        res=1
        for i in range(1000000):
            res*=i
            next(g)
            print('task2')
    
    start=time.time()
    task2()
    stop=time.time()
    print(stop-start)
    
    一、单线程下实现遇到IO切换
    1、用greenlet(封装yield,遇到IO不自动切)
    from greenlet import greenlet
    import time
    
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(30)
        g2.switch('alex')  #只在第一次切换时传值
        print('%s eat 2' %name)
        g2.switch()
    def play(name):
        print('%s play 1' %name)
        g1.switch()
        print('%s play 2' %name)
    
    g1=greenlet(eat)
    g2=greenlet(play)
    g1.switch('egon')
    
    2、用gevent模块(封装greenlet,不处理的话,遇到自己的IO才主动切)
    import gevent
    
    def eat(name):
        print('%s eat 1' %name)
        gevent.sleep(5)  #换成time.sleep(5),不会自动切
        print('%s eat 2' %name)
    def play(name):
        print('%s play 1' %name)
        gevent.sleep(3)
        print('%s play 2' %name)
    
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,'alex')
    
    # gevent.sleep(100)
    # g1.join()
    # g2.join()
    gevent.joinall([g1,g2])
    
    3、用gevent模块(封装greenlet,处理的话,遇到其他IO也主动切)
    from gevent import monkey;monkey.patch_all()
    from threading import current_thread
    import gevent
    import time
    
    def eat():
        print('%s eat 1' %current_thread().name)
        time.sleep(5)
        print('%s eat 2' %current_thread().name)
    def play():
        print('%s play 1' %current_thread().name)
        time.sleep(3)
        print('%s play 2' %current_thread().name)
    
    g1=gevent.spawn(eat)
    g2=gevent.spawn(play)
    
    # gevent.sleep(100)
    # g1.join()
    # g2.join()
    print(current_thread().name)
    gevent.joinall([g1,g2])
  • 相关阅读:
    echarts 报表使用
    Eclipse Java注释模板设置详解
    简单实现支付密码输入框 By HL
    一个label 混搭不同颜色,不同字体的文字.. by 徐
    有关MVC设计模式 #DF
    自定义粘贴板-陈鹏
    TableView 常用技巧与功能详解
    ios 类别和扩展-赵小波
    推荐一本书--《浪潮之巅》(完整版的哦)----董鑫
    iOS block的用法 by -- 周傅琦君
  • 原文地址:https://www.cnblogs.com/zhaijihai/p/9629986.html
Copyright © 2020-2023  润新知