引子:

  之前学习过了,线程,进程的概念,知道了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位.按道理来说我们已经算是把CPU的利用率提高很多了.但是我们知道无论是创建多进程还是创建多线程来解决问题,都要消耗一定的时间来创建进程,创建线程,以及管理他们之间的切换.

  随着我们对于效率的最求不断提高,基于单线程来实现并发又成为一个新的课题.即只用一个主线程的情况下实现并发.这样就可以节省创建线程进程所消耗的时间.

  并发的本质: 切换+保存状态

  cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是任务发生了阻塞,另外一种情况是该任务计算时间过长

  

  对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。

  协成的本质就是在单线程下,由用户自己控制一个任务遇到IO阻塞就切换另一个任务去执行,一次来提升效率.

  1. #可以控制对个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行.
  2. #作为1的补充;可以检测IO操作,在遇到IO操作的情况下才发生切换

协程介绍:

协程:是单线程下的并发,又称为为线程. 协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的.

协程特点:

  必须在只有一个单线程里实现并发

  修改共享数据不需加锁

  用户程序里自己保存多个控制流的上下文栈

  一个协程遇到IO操作自动切换到其他协程

Greenlet模块

安装:pip3 install greenlet

  1. from greenlet import greenlet
  2.  
  3. def eat(name):
  4. print('%s eat 1'%name)
  5. g2.switch('egon')
  6. print('%s eat 2'%name)
  7. g2.switch()
  8. def play(name):
  9. print('%s play 1' %name)
  10. g1.switch
  11. print('%s play 2' %name)
  12.  
  13. g1 = greenlet(eat)
  14. g2 = greenlet(play)
  15.  
  16. g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要

greenlet实现状态切换

Gevent模块

安装:pip3 install gevent

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

  1. import gevent
  2. def eat(name):
  3. print('%s eat 1' %name)
  4. gevent.sleep(2)
  5. print('%s eat 2' %name)
  6.  
  7. def play(name):
  8. print('%s play 1' %name)
  9. gevent.sleep(1)
  10. print('%s play 2' %name)
  11.  
  12. g1=gevent.spawn(eat,'egon')
  13. g2=gevent.spawn(play,name='egon')
  14. g1.join()
  15. g2.join()
  16. #或者gevent.joinall([g1,g2])
  17. print('主')

遇到IO主动切换

IO多路复用

IO多路复用作用:检查多个socket是否已经发生变化(是否连接成功/是否已经获取数据)(可读/可写)

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。

三种模式:

  select:最多1024个socket;循环去检测。

  poll:不限制监听socket个数;循环去检测(水平触发)。

  epoll:不限制监听socket个数;回调方式(边缘触发)。

基于IO多路复用+socket实现并发请求:

IO多路复用

socket 非阻塞(不等待)

异步:执行完某个任务后自动调用我给它的函数

Python中开源  基于事件循环实现的异步非阻塞框架  Twisted

  1. import socket
  2. import requests
  3.  
  4. # 方式一
  5. ret = requests.get('https://www.baidu.com/s?wd=alex')
  6.  
  7. # 方式二
  8. client = socket.socket()
  9.  
  10. # 百度创建连接: 阻塞
  11. client.connect(('www.baidu.com',80))
  12.  
  13. # 问百度我要什么?
  14. client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
  15.  
  16. # 我等着接收百度给我的回复
  17. chunk_list = []
  18. while True:
  19. chunk = client.recv(8096)
  20. if not chunk:
  21. break
  22. chunk_list.append(chunk)
  23.  
  24. body = b''.join(chunk_list)
  25. print(body.decode('utf-8'))

socket发生请求

  1. import socket
  2. import requests
  3.  
  4. # 方式一
  5. key_list = ['alex','db','sb']
  6. for item in key_list:
  7. ret = requests.get('https://www.baidu.com/s?wd=%s' %item)
  8.  
  9. # 方式二
  10. def get_data(key):
  11. # 方式二
  12. client = socket.socket()
  13.  
  14. # 百度创建连接: 阻塞
  15. client.connect(('www.baidu.com',80))
  16.  
  17. # 问百度我要什么?
  18. client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
  19.  
  20. # 我等着接收百度给我的回复
  21. chunk_list = []
  22. while True:
  23. chunk = client.recv(8096)
  24. if not chunk:
  25. break
  26. chunk_list.append(chunk)
  27.  
  28. body = b''.join(chunk_list)
  29. print(body.decode('utf-8'))
  30.  
  31. key_list = ['alex','db','sb']
  32. for item in key_list:
  33. get_data(item)

单线程解决并发

  1. import threading
  2.  
  3. key_list = ['alex','db','sb']
  4. for item in key_list:
  5. t = threading.Thread(target=get_data,args=(item,))
  6. t.start()

解决并发,多线程

提高并发方案:

--多进程

--多线程

--异步非阻塞模块(Twisted)  scrapy框架(单线程完成并发)

  1. import socket
  2. import select
  3.  
  4. client1 = socket.socket()
  5. client1.setblocking(False) # 百度创建连接: 非阻塞
  6.  
  7. try:
  8. client1.connect(('www.baidu.com',80))
  9. except BlockingIOError as e:
  10. pass
  11.  
  12. client2 = socket.socket()
  13. client2.setblocking(False) # 百度创建连接: 非阻塞
  14. try:
  15. client2.connect(('www.sogou.com',80))
  16. except BlockingIOError as e:
  17. pass
  18.  
  19. client3 = socket.socket()
  20. client3.setblocking(False) # 百度创建连接: 非阻塞
  21. try:
  22. client3.connect(('www.oldboyedu.com',80))
  23. except BlockingIOError as e:
  24. pass
  25.  
  26. socket_list = [client1,client2,client3]
  27. conn_list = [client1,client2,client3]
  28.  
  29. while True:
  30. rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)
  31. # wlist中表示已经连接成功的socket对象
  32. for sk in wlist:
  33. if sk == client1:
  34. sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
  35. elif sk==client2:
  36. sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')
  37. else:
  38. sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')
  39. conn_list.remove(sk)
  40. for sk in rlist:
  41. chunk_list = []
  42. while True:
  43. try:
  44. chunk = sk.recv(8096)
  45. if not chunk:
  46. break
  47. chunk_list.append(chunk)
  48. except BlockingIOError as e:
  49. break
  50. body = b''.join(chunk_list)
  51. # print(body.decode('utf-8'))
  52. print('------------>',body)
  53. sk.close()
  54. socket_list.remove(sk)
  55. if not socket_list:
  56. break

单线程完成并发

什么是异步非阻塞?

  --非阻塞:不等待

    比如创建socket对某个地址进行connect,获取接收数据recv时默认都会等待(连接成功或接收到数据),才执行后续操作.如果设置setblocking(False),以上两个过程就不在等待,到时会报BlockingIOError的错误,只要捕获即可.

  --异步,通知,执行完成后自动执行回调函数或自动执行某些操作(通知)

    比如做爬虫中向某个地址baidu.com发送请求,当请求执行完成之后自动执行回调函数.

什么是同步阻塞?

--阻塞:等

--同步:按照顺序逐步执行

  1. key_list = ['alex','db','sb']
  2. for item in key_list:
  3. ret = requests.get('https://www.baidu.com/s?wd=%s' %item)
  4. print(ret.text)

  

Python之路--协程/IO多路复用的更多相关文章

  1. 协程IO多路复用

    协程:单线程下实现并发并发:伪并行,遇到IO就切换,单核下多个任务之间切换执行,给你的效果就是貌似你的几个程序在同时执行.提高效率任务切换 + 保存状态并行:多核cpu,真正的同时执行串行:一个任务执 ...

  2. day 35 协程 IO多路复用

    0.基于socket发送Http请求 import socket import requests # 方式一 ret = requests.get('https://www.baidu.com/s?w ...

  3. python之路---协程

    阅读目录 一 引子 二 协程介绍 三 Greenlet模块 四 Gevent模块 引子 之前我们学习了线程.进程的概念,了解了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位.按道理来 ...

  4. 12_进程,线程,协程,IO多路复用的区别

    1.进程 1.进程可以使用计算机多核 2.进程是资源分配的单位 3.进程的创建要比线程消耗更多的资源效率很低 4.进程空间独立,数据安全性跟好操作有专门的进程间通信方式 5.一个进程可以包含多个线程, ...

  5. 协程 IO多路复用

    -----------------------------------------------------------------试试并非受罪,问问并不吃亏.善于发问的人,知识丰富. # # ---- ...

  6. python线程、协程、I/O多路复用

    目录: 并发多线程 协程 I/O多路复用(未完成,待续) 一.并发多线程 1.线程简述: 一条流水线的执行过程是一个线程,一条流水线必须属于一个车间,一个车间的运行过程就是一个进程(一个进程内至少一个 ...

  7. Python学习笔记整理总结【网络编程】【线程/进程/协程/IO多路模型/select/poll/epoll/selector】

    一.socket(单链接) 1.socket:应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socke ...

  8. 第十一章:Python高级编程-协程和异步IO

    第十一章:Python高级编程-协程和异步IO Python3高级核心技术97讲 笔记 目录 第十一章:Python高级编程-协程和异步IO 11.1 并发.并行.同步.异步.阻塞.非阻塞 11.2 ...

  9. Python之路,Day10 - 异步IO\数据库\队列\缓存

    Python之路,Day9 - 异步IO\数据库\队列\缓存   本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitM ...

随机推荐

  1. springboot项目中进行并发测试

    一 利用工具包: <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...

  2. tensorflow/models 下面的data_augment_options的random_image_scale

    这个random_image_scale应该是改变整个图片的大小,而不是“box”图片的大小

  3. Vuex持久化存储之vuex-persist

    在引入mapMutations时报错,解决方法: 1:npm install --save-dev babel-plugin-transform-object-rest-spread 2:在packa ...

  4. 匿名/局部内部类访问局部变量时,为什么局部变量必须加final

    我们都知道方法中的匿名/局部内部类是能够访问同一个方法中的局部变量的,但是为什么局部变量要加上一个final呢? 首先我们来研究一下变量生命周期的问题,局部变量的生命周期是当该方法被调用时在栈中被创建 ...

  5. 左神算法书籍《程序员代码面试指南》——2_02在单链表和双链表中删除倒数第k个字节

    [题目]分别实现两个函数,一个可以删除单链表中倒数第K个节点,另一个可以删除双链表中倒数第K个节点.[要求]如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1).[题解]从头遍历链表, ...

  6. Jmeter分布式测试过程中遇到的问题及摘抄前辈问题汇总

    遇到的常见问题: 1.在Controller端上控制某台机器Run,提示"Bad call to remote host". 解决方法:检查被控制机器上的jmeter-server ...

  7. JSP - (Java Server Pages) - Java服务器界面

    JSP简介: 在HTML中嵌入Java脚本代码,由应用服务器中的JSP引擎来编译和执行嵌入的Java脚本代码,然后将生成的整个页面信息返回给客户端: 一个JSP页面包含:静态内容(HTML静态文本), ...

  8. virtualenv简单使用

    前提 在开发过程中,经常需要使用各种第三方库,而且python又提供了pip,easy_install等工具来简化库的安装,所以很容易就会在系统python的site-packages目录中装满各种各 ...

  9. 使用 windows 批处理指令(BAT文件)进行文件删除、复制操作

    以下是做文件删除和复制的批处理指令 ::替换文件需要添加 /y 参数才能直接替换.不然会出现提示是否替换. ::复制Axis2Implementation和WebServices编译后的文件到tomc ...

  10. HYSBZ 1015/BZOJ1015 星球大战starwar

    Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过 ...