参考博客:http://www.cnblogs.com/alex3714/articles/5248247.html

一、前言

  Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

二、Gevent

  1. import gevent
  2.  
  3. def func1():
  4. print('running in func1...')
  5. gevent.sleep(2) # I/O中断2秒
  6. print('Explicit context switch to func1 again...')
  7.  
  8. def func2():
  9. print('Explicit context func2...')
  10. gevent.sleep(1)
  11. print('Implicit context switch to func2 again...')
  12.  
  13. gevent.joinall([
  14. gevent.spawn(func1),
  15. gevent.spawn(func2),
  16. ])

  

  1. import gevent
  2.  
  3. def func1():
  4. print('running in func1...')
  5. gevent.sleep(2)
  6. print('Explicit context switch to func1 again...')
  7.  
  8. def func2():
  9. print('Explicit context func2...')
  10. gevent.sleep(1)
  11. print('Implicit context switch to func1 again...')
  12.  
  13. def func3():
  14. print('running in func3...')
  15. gevent.sleep(0) # 表示中断,没有时间
  16. print('running func3 again...')
  17.  
  18. gevent.joinall([
  19. gevent.spawn(func1),
  20. gevent.spawn(func2),
  21. gevent.spawn(func3),
  22. ])

  

三、遇到I/O阻塞时自动切换任务

  简单的gevent并发爬网页  

  1. # -*- coding: UTF-8 -*-
  2.  
  3. from urllib import request
  4. import time
  5. from gevent import monkey
  6. import gevent
  7.  
  8. monkey.patch_all()
  9. # gevent并不能识别urllib 的I/O中断,monkey.patch_all会给所有I/O中断做标签
  10.  
  11. def f(url):
  12. print('Get: %s' % url)
  13. resp = request.urlopen(url)
  14. data = resp.read()
  15. print('%d bytes received from %s.' % (len(data), url))
  16.  
  17. # 同步处理
  18. urls = [
  19. 'https://www.python.org/',
  20. 'https://www.yahoo.com/',
  21. 'https://github.com/'
  22. ]
  23.  
  24. time_start = time.time()
  25. for url in urls:
  26. f(url)
  27. print('同步take %s seconds' % (time.time() - time_start))
  28.  
  29. # 异步处理
  30. async_time = time.time()
  31. gevent.joinall([
  32. gevent.spawn(f, 'https://www.python.org/'),
  33. gevent.spawn(f, 'https://www.yahoo.com/'),
  34. gevent.spawn(f, 'https://github.com/')
  35. ])
  36. print('异步take %s seconds' % (time.time() - async_time))

  结果:

  1. Get: https://www.python.org/
  2. 48956 bytes received from https://www.python.org/.
  3. Get: https://www.yahoo.com/
  4. 517389 bytes received from https://www.yahoo.com/.
  5. Get: https://github.com/
  6. 51473 bytes received from https://github.com/.
  7. 同步take 4.6533966064453125 seconds
  8. Get: https://www.python.org/
  9. Get: https://www.yahoo.com/
  10. Get: https://github.com/
  11. 48956 bytes received from https://www.python.org/.
  12. 521617 bytes received from https://www.yahoo.com/.
  13. 51473 bytes received from https://github.com/.
  14. 异步take 1.916191816329956 seconds
  15.  
  16. # 可以看到两者之间时间差别还是很大的

  如果没有monkey.patch_all(),程序也能正常运行,但是同步和异步时间基本没有差别,因为gevent并不知道urllib的I/O中断,所以也不会自动切换,所以哪怕使用了gevent,程序也是同步执行(串行)。而monkey.patch_all()就可以为所有urllib中的I/O中断做标记,gevent遇到这些中断就会自动切换,执行其他没有I/O操作的程序。

四、gevent实现单线程下的多socket并发

  server side

  1. import sys
  2. import socket
  3. import time
  4. import gevent
  5.  
  6. from gevent import socket,monkey
  7. monkey.patch_all()
  8.  
  9. def server(port):
  10. s = socket.socket()
  11. s.bind(('0.0.0.0', port))
  12. s.listen(500)
  13. while True:
  14. cli, addr = s.accept()
  15. gevent.spawn(handle_request, cli)
  16.  
  17. def handle_request(conn):
  18. try:
  19. while True:
  20. data = conn.recv(1024)
  21. print("recv:", data)
  22. conn.send(data)
  23. if not data:
  24. conn.shutdown(socket.SHUT_WR)
  25.  
  26. except Exception as ex:
  27. print(ex)
  28. finally:
  29. conn.close()
  30. if __name__ == '__main__':
  31. server(8001)

  client side

  1. import socket
  2.  
  3. HOST = 'localhost' # The remote host
  4. PORT = 8001 # The same port as used by the server
  5. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  6. s.connect((HOST, PORT))
  7. while True:
  8. msg = bytes(input(">>:"),encoding="utf8")
  9. s.sendall(msg)
  10. data = s.recv(1024)
  11. #print(data)
  12.  
  13. print('Received', repr(data))
  14. s.close()

  

  1. # 并发100 个socket连接
  2.  
  3. import socket
  4. import threading
  5.  
  6. def sock_conn():
  7.  
  8. client = socket.socket()
  9.  
  10. client.connect(("localhost",8001))
  11. count = 0
  12. while True:
  13. #msg = input(">>:").strip()
  14. #if len(msg) == 0:continue
  15. client.send( ("hello %s" %count).encode("utf-8"))
  16.  
  17. data = client.recv(1024)
  18.  
  19. print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果
  20. count +=1
  21. client.close()
  22.  
  23. for i in range(100):
  24. t = threading.Thread(target=sock_conn)
  25. t.start()

  

协程-遇到I/O自动切换的更多相关文章

  1. 【python】-- 协程介绍及基本示例、协程遇到IO操作自动切换、协程(gevent)并发爬网页

    协程介绍及基本示例 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是协程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他 ...

  2. 利用协程爬网页,自动切换io 精典案例:

    首先Python提供的协程库gevent好像并不能知道那些程序使用了io  所以要加一个补丁,mondey,以下同步和异步各爬一次的案例 : , from urllib import requesti ...

  3. flask之分析线程和协程

    flask之分析线程和协程 01 思考:每个请求之间的关系 我们每一个请求进来的时候都开一个进程肯定不合理,那么如果每一个请求进来都是串行的,那么根本实现不了并发,所以我们假定每一个请求进来使用的是线 ...

  4. Flask 之分析线程和协程

    目录 flask之分析线程和协程 01 思考:每个请求之间的关系 02 threading.local 03 通过字典自定义threading.local 04 通过setattr和getattr实现 ...

  5. 网络编程基础--协程--greenlet切换---gevent自动识别 IO ---

    协程: 1 单线程来实现并发---协程: 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程, 即协程是由用户程序自己控制调度的 只 ...

  6. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  7. 并发异步编程之争:协程(asyncio)到底需不需要加锁?(线程/协程安全/挂起/主动切换)Python3

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_208 协程与线程向来焦孟不离,但事实上是,线程更被我们所熟知,在Python编程领域,单核同时间内只能有一个线程运行,这并不是什么 ...

  8. 异步、+回调机制、线程queue、线程Event、协程、单线程实现遇到IO切换

    # from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor # import requests # import o ...

  9. PHP下的异步尝试三:协程的PHP版thunkify自动执行器

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunk ...

随机推荐

  1. redis与mysql性能对比、redis缓存穿透、缓存雪崩

    写在开始 redis是一个基于内存hash结构的缓存型db.其优势在于速读写能力碾压mysql.由于其为基于内存的db所以存储数据量是受限的. redis性能 redis读写性能测试redis官网测试 ...

  2. java-HttpGetPost-图片字节流上传

    在java程序开发中经常用到与服务端的交互工作,主要的就是传递相应的参数请求从而获取到对应的结果加以处理 可以使用Get请求与Post请求,注意!这里的Get请求不是通过浏览器界面而是在程序代码中设置 ...

  3. 由A到D中间可不止“B、C”

    在电子信息系统的学习中,我们或许早就被告知现实世界是模拟的,而数字化的模拟世界则越来越展现更多的风采.但是所谓的数字和模拟只是相对的而已,你可以把模拟量当做无穷数字量的组合,也可以把数字量当做具有不同 ...

  4. CF刷刷水题找自信1

    CF 1108A Two distinct points 题目意思:给你两个线段的起点和终点,让你给出两个不同的点,这两点分别处于两个不同的线段之中.解题思路:题目说如果存在多种可能的点,随意一组答案 ...

  5. 团队Alpha冲刺(二)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:丹丹 组员7:家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示组内 ...

  6. myeclipse和ecplise中安装git插件的问题

    我的myeclipse10.7和ecplise helis一直安装不了git插件,myeclipse中details说我的myeclipse少了team_features等之类文件,helis的情况大 ...

  7. EasyUI中DataGrid构建复合表头

    在使用easyui的DataGrid控件时,构建复合表头就显得非常简单了.只需要在使用columns属性时通过数组的方式编写列名即可.如我们需要构建成一个如下的表头: Columns的代码如下: co ...

  8. 一个form表单,多个提交按钮

    技巧就是把提交的input的类型改成button!这样就可以实现多个按钮提交! 以下是案例: <form action="" id="tijiao"> ...

  9. MySql中的varchar类型

    转载:http://www.cnblogs.com/doit8791/archive/2012/05/28/2522556.html 1.varchar类型的变化 MySQL 数据库的varchar类 ...

  10. Django之ORM对数据库操作

    基本操作 <1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs): ...