• Python-并发编程(协程)


    使用gevent模块实现并发

    例子

    服务器
    from gevent import monkey#往常扩展模块要在内置模块下面,但是这个扩展模块必须写下上面
    monkey.patch_all()
    import gevent
    import socket
    
    def talk():
        while True:
            conn.send(b'helle')
            msg = conn.recv(1024)
            print(msg)
        conn.close()
    sk=socket.socket()
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    while True:
        conn,addr = sk.accept()
        gevent.spawn(talk,conn)#实现并发
    sk.close()
    #=====================
    客户端
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',9000))
    
    while True:
        msg=sk.recv(1024)
        print(msg)
        ret = input('>>>').encode('utf-8')
        sk.send(ret)
    sk.close()

    协程

    # 协程和线程有什么关系?
    #协程是线程内的一个单位
    #线程已经是CPU调度的最小单位了
    #协程--又叫纤程
    #Cpython解释器下的多线程,在同一时刻只能有一个线程访问CPU
    #所以多线程能发挥显著作用的时候就是不太耗CPU,涉及到的计算操作少
    #IO高,大部分时间都用再等待上
    
    #协程的本质是一条线程的一部分---下图是解释什么是协程,就是相当于是并发的样子,两个线程

    #线程: #正常的线程,遇到阻塞就停下来,知道阻塞事件结束,才继续 # 协程: #利用了协程,就把线程分成了好几段,在一个任务出现阻塞的时候,自动的切换到 #另一个任务去执行,这些所有的事在一个线程里面完成。 #如果这个程序从头到尾没有IO,没有阻塞,程序根本就不会在多个任务之间切换 #就是一个顺序执行的,协程对于高计算型的代码没有用 #对于高IO型 #线程 #是计算机中最小的CPU调度单位 #协程 # --不用考虑数据安全,因为就在一个线程上执行 # --协程是如何调度的? #协程不能被操作系统调度 #是用户级别来调度的,就是gevent调度的 #协程的调度速度比线程还快 #协程的调度由于是Python代码级别的而不是操作系统级别的,所以用户的可控制性更强,降低了操作系统的工作量

    协程到底是怎么实现程序之间的切换的

    #生成器
    def func(): #func是一个任务
        print(123)
        yield 1
        print(456)
        yield 2
        print(789)
        yield 3
    g =func()
    print(g.__next__())#程序之间的切换,代码会记住执行的位置,等待下一次执行
    
    def wahaha(g):#wahaha是一个任务
        for i in g:
            print(i)
    g = func()
    wahaha(g)
    #两个任务交替执行---说明程序之间的切换是可以控制的
    import greenlet
    #两个模块gevent,greenLet
    #gevent是依赖greenLet 协程之间的切换是由greenLet完成的
    from greenlet import greenlet
    
    def eat(name):
        print('%s eat 1' %name)
        g2.switch('egon')
        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')#可以在第一次switch时传入参数,以后都不需要

    #程序级别的切换

      #切换并不能让你的程序效率提高,反而会下降

    #如果遇到IO才切换,那么就能提高效率,这个IO切换是由gevent借助greenLet来实现的

    import gevent#能够在遇到自己认识的io操作的时候,主动调用greenLet中的switch来切换到其他的任务已提高程序的效率
    def eat(name):
        print('%s eat 1' %name)
        gevent.sleep(2)
        print('%s eat 2' %name)
    
    def play(name):
        print('%s play 1' %name)
        gevent.sleep(1)
        print('%s play 2' %name)
    
    
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,name='egon')
    g1.join()
    g2.join()
    #或者gevent.joinall([g1,g2])
    print('')

    如果吧.join()取消,那么只会打印出‘主’

    import gevent#能够在遇到自己认识的io操作的时候,主动调用greenLet中的switch来切换到其他的任务已提高程序的效率
    def eat(name):
        print('%s eat 1' %name)
        gevent.sleep(2)
        print('%s eat 2' %name)
    
    def play(name):
        print('%s play 1' %name)
        gevent.sleep(1)
        print('%s play 2' %name)
    
    
    g1=gevent.spawn(eat,'egon') #就是发布了任务
    g2=gevent.spawn(play,name='egon')#就是发布了任务    
    gevent.sleep(1) #使用这个睡1S,能够打印出部分
    #g1.join() #等待g1任务执行完毕,阻塞,帮助你完成g1中函数的全部内容
    #g2.join()
    #或者gevent.joinall([g1,g2]) #这个可以实现上面两个.join()的功能
    print('')

    gevent并不认识不在这个模块中的IO操作time.sleep(2)

    import time
    import gevent
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(2)
        print('%s eat 2' %name)
    
    def play(name):
        print('%s play 1' %name)
        time.sleep(1)
        print('%s play 2' %name)
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,name='egon')
    g1.join()
    g2.join()
    #或者gevent.joinall([g1,g2])
    print('')

    解决办法

    from gevent import monkey
    monkey.patch_all()#这个作用就是将gevent模块之外的sleep归为我认识的IO操作,就能认识time模块中的sleep了
    import time
    import gevent
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(2)
        print('%s eat 2' %name)
    
    def play(name):
        print('%s play 1' %name)
        time.sleep(1)
        print('%s play 2' %name)
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,name='egon')
    g1.join()
    g2.join()
    #或者gevent.joinall([g1,g2])
    print('')

    验证确定是否是协程

    from gevent import monkey
    monkey.patch_all()#这个作用就是将gevent模块之外的sleep归为我认识的IO操作,就能认识time模块中的sleep了
    import time
    import gevent
    from threading import currentThread
    def eat(name):
        print(currentThread())
        print('%s eat 1' %name)
        time.sleep(2)
        print('%s eat 2' %name)
    
    def play(name):
        print(currentThread())
        print('%s play 1' %name)
        time.sleep(1)
        print('%s play 2' %name)
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,name='egon')
    g1.join()
    g2.join()
    #或者gevent.joinall([g1,g2])
    print('')

    如何解决并发问题

    #解决并发问题??
    #多进程   高计算型,浪费操作系统和资源,可以利用多核
    #多线程  高IO型,也会给操作系统添加负担,会占用比进程少的资源
                #受到GIL的影响
                # 不能利用多核
    #协程    高IO型,完全不会给操作系统添加负担,几乎不占资源
                #始终不能利用多核
    
    #多进程+多线程 :建议不超过100条
    #多进程+多线程+协程 :最多接收50000条,这个是多快的
        #假设是4*CPU
        #多进程 CPU+1=5
        #多线程 CPU*5 = 20
        #协程   500个协程

    验证协程是否能执行500个请求---使用协程接受500个并发

    #服务器
    from gevent import monkey#往常扩展模块要在内置模块下面,但是这个扩展模块必须写下上面
    monkey.patch_all()
    import gevent
    import socket
    
    def talk():
        while True:
            conn.send(b'helle')
            msg = conn.recv(1024)
            print(msg)
        conn.close()
    sk=socket.socket()
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    while True:
        conn,addr = sk.accept()
        gevent.spawn(talk,conn)#实现并发
    sk.close()
    #客户端
    import socket
    from threading import Thread
    def client():
        sk = socket.socket()
        sk.connect(('127.0.0.1',9000))
    
        while True:
            msg=sk.recv(1024)
            print(msg)
            ret = input('>>>').encode('utf-8')
            sk.send(ret)
            sk.close()
    if __name__ == '__main__':
        for i in range(500):
            Thread(target=client).start()
     
  • 相关阅读:
    iOS开发之单例模式
    XCode 安装 Alcatraz包管理器失败的处理
    iOS "此证书由未知颁发机构签名"此问题的解决方法
    Android WebView 使用
    BaseActivity
    定时周期执行指定的任务 ScheduledExecutorService
    SQLite数据库浅谈
    android 图片缓存
    Android之drawable state各个属性详解
    Android应用中如何启动另一个应用
  • 原文地址:https://www.cnblogs.com/xiao-xuan-feng/p/14420713.html
Copyright © 2020-2023  润新知