python之路---并发编程之协程&gevent模块

 

python之路---并发编程之协程&gevent模块

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ltfdsy/article/details/82592522

协程

想要在单线程下实现并发,又称之为微线程/纤程,是一种用户态的轻量级线程(由应用程序自己控制调度)

如何在单线程下实现并发

什么是并发

看起来像同时运行(实际上同一时刻只有一个任务在运行),本质是:操作系统控制cpu进行切换+保存状态

程序运行的三种状态

阻塞:当任务出现IO时,发生阻塞(cpu被操作系统剥离)

就绪:当发生阻塞的任务出现有效输出时,由阻塞进入就绪/当一个优先级更高的任务出现时,由运行进入就绪态

运行:当处于就绪态的任务被分配到cpu时,由就绪进入运行

此外

1.一个任务不可能一直占用着CPU,我们能控制的就是将程序尽可能的保持在就绪态--->减少IO

2.遇到IO的切换是提升效率的,单纯的IO切换反而降低效率

我们说一个C/S架构的套接字软件,其服务端应该满足支持并发,对于这种多IO的任务,应该使用多线程,但是,这种并发的实现没有解决IO问题,当一个线程出现IO时,整个线程就发生了阻塞(只是没有影响其它线程而已)

如何实现利用率的最大化

通过在一个线程内多个任务的切换+保存状态(在应用程序级别,而非操作系统级别实现,由一个正在IO的任务切换到其它计算的任务) --->这样使单个线程时刻保持在就绪状态(将线程内的IO行为隐藏,让操作系统尽可能的将cpu分配给自己)

注意

1.python的线程是属于内核级别的,由操作系统控制,调度(进行强制切换),而线程是应用程序级别的,由应用程序自己控制调度

2.操作系统只有线程与进程的概念(操作系统调度的是线程)

优点

1.应用程序级别的切换开销更小

2.在单线程下实现并发,最大限度的利用CPU

缺点

1.一旦引入协程的概念,就得监测一个任务下的所以IO行为,实现遇到IO就切换,一旦有一个地方没切,整个线程就阻塞在了原地

2.本质还是一个线程,无法利用多核

如何实现单线程下的并发

1.可以利用python下的gevent模块

2.IO模型

对于使用gevent模块,首先要下载

pip insatll gevent
  1.  
    from gevent import monkey;monkey.patch_all()
  2.  
    import gevent
  3.  
    import time
  4.  
     
  5.  
     
  6.  
    def task1():
  7.  
    print('task1 is running...')
  8.  
    time.sleep(1)
  9.  
    print('task1 is done...')
  10.  
     
  11.  
     
  12.  
    def task2():
  13.  
    print('task2 is running...')
  14.  
    time.sleep(3)
  15.  
    print('task2 is done...')
  16.  
     
  17.  
     
  18.  
    if __name__ == '__main__':
  19.  
    p1 = gevent.spawn(task1)
  20.  
    p2 = gevent.spawn(task2) # 异步提交任务
  21.  
     
  22.  
    gevent.joinall([p1,p2])
  23.  
    print('>>>')

利用协程实现单线程下的简单并发套接字

  1.  
    # Server
  2.  
     
  3.  
    from socket import *
  4.  
    from gevent import monkey, spawn
  5.  
     
  6.  
    monkey.patch_all()
  7.  
    sever_address = ('127.0.0.1', 8080)
  8.  
     
  9.  
     
  10.  
    def task(conn):
  11.  
    while True:
  12.  
    try:
  13.  
    data = conn.recv(1024)
  14.  
    if len(data) == 0:break
  15.  
    conn.send(data.upper())
  16.  
    except ConnectionResetError:
  17.  
    break
  18.  
     
  19.  
     
  20.  
    def build():
  21.  
    socket_obj = socket(AF_INET,SOCK_STREAM)
  22.  
    socket_obj.bind(sever_address)
  23.  
    socket_obj.listen()
  24.  
    print('server is reading...')
  25.  
    while True:
  26.  
    conn, client_address = socket_obj.accept()
  27.  
    print('client is arrive...',client_address)
  28.  
    spawn(task,conn)
  29.  
     
  30.  
     
  31.  
    if __name__ == '__main__':
  32.  
    obj=spawn(build)
  33.  
    obj.start()
  34.  
    obj.join()
  1.  
    # Client
  2.  
     
  3.  
    from socket import *
  4.  
    from threading import Thread,current_thread
  5.  
     
  6.  
     
  7.  
    def task(address):
  8.  
    obj = socket(AF_INET,SOCK_STREAM)
  9.  
    obj.connect(address)
  10.  
    while True:
  11.  
    print(current_thread().name)
  12.  
    obj.send(b'hello world')
  13.  
     
  14.  
     
  15.  
    for i in range(500):
  16.  
    t = Thread(target=task,args=(('127.0.0.1',8080),))
  17.  
    t.start()
  18.  
     

 

 

 

posted on 2019-11-21 12:47  曹明  阅读(121)  评论(0)    收藏  举报