引子:

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

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

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

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

  

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

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

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

协程介绍:

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

协程特点:

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

  修改共享数据不需加锁

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

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

Greenlet模块

安装:pip3 install greenlet

from greenlet import greenletdef 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.switchprint('%s play 2' %name)g1 = greenlet(eat)
g2 = greenlet(play)g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要

greenlet实现状态切换

Gevent模块

安装:pip3 install gevent

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

import gevent
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('主')

遇到IO主动切换

IO多路复用

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

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

三种模式:

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

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

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

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

IO多路复用

socket 非阻塞(不等待)

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

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

import socket
import requests# 方式一
ret = requests.get('https://www.baidu.com/s?wd=alex')# 方式二
client = socket.socket()# 百度创建连接: 阻塞
client.connect(('www.baidu.com',80))# 问百度我要什么?
client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')# 我等着接收百度给我的回复
chunk_list = []
while True:chunk = client.recv(8096)if not chunk:breakchunk_list.append(chunk)body = b''.join(chunk_list)
print(body.decode('utf-8'))

socket发生请求

import socket
import requests# 方式一
key_list = ['alex','db','sb']
for item in key_list:ret = requests.get('https://www.baidu.com/s?wd=%s' %item)# 方式二
def get_data(key):# 方式二client = socket.socket()# 百度创建连接: 阻塞client.connect(('www.baidu.com',80))# 问百度我要什么?client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')# 我等着接收百度给我的回复chunk_list = []while True:chunk = client.recv(8096)if not chunk:breakchunk_list.append(chunk)body = b''.join(chunk_list)print(body.decode('utf-8'))key_list = ['alex','db','sb']
for item in key_list:get_data(item)

单线程解决并发

import threadingkey_list = ['alex','db','sb']
for item in key_list:t = threading.Thread(target=get_data,args=(item,))t.start()

解决并发,多线程

提高并发方案:

--多进程

--多线程

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

import socket
import selectclient1 = socket.socket()
client1.setblocking(False) # 百度创建连接: 非阻塞try:client1.connect(('www.baidu.com',80))
except BlockingIOError as e:passclient2 = socket.socket()
client2.setblocking(False) # 百度创建连接: 非阻塞
try:client2.connect(('www.sogou.com',80))
except BlockingIOError as e:passclient3 = socket.socket()
client3.setblocking(False) # 百度创建连接: 非阻塞
try:client3.connect(('www.oldboyedu.com',80))
except BlockingIOError as e:passsocket_list = [client1,client2,client3]
conn_list = [client1,client2,client3]while True:rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)# wlist中表示已经连接成功的socket对象for sk in wlist:if sk == client1:sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')elif sk==client2:sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')else:sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')conn_list.remove(sk)for sk in rlist:chunk_list = []while True:try:chunk = sk.recv(8096)if not chunk:breakchunk_list.append(chunk)except BlockingIOError as e:breakbody = b''.join(chunk_list)# print(body.decode('utf-8'))print('------------>',body)sk.close()socket_list.remove(sk)if not socket_list:break

单线程完成并发

什么是异步非阻塞?

  --非阻塞:不等待

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

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

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

什么是同步阻塞?

--阻塞:等

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

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

  

转载于:https://www.cnblogs.com/wangjun187197/p/9642429.html

Python之路--协程/IO多路复用相关推荐

  1. Python与Golang协程异同

    背景知识 这里先给出一些常用的知识点简要说明,以便理解后面的文章内容. 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定 ...

  2. python中的协程(二)

    协程 1.协程: 单线程实现并发 在应用程序里控制多个任务的切换+保存状态 优点: 应用程序级别速度要远远高于操作系统的切换 缺点: 多个任务一旦有一个阻塞没有切,整个线程都阻塞在原地,该线程内的其他 ...

  3. python并发之协程_python并发编程之协程

    一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去 ...

  4. Python中的协程

    Python中的协程 文章目录 Python中的协程 一.什么是协程 1.概念 2.协程的好处 3.缺点 二.了解协程的过程 1.yield工作原理 2.协程在运行过程中有四个状态: 3.预激协程的装 ...

  5. Python并发之协程gevent基础(5)

    1,gevent介绍 gevent是第三方库,通过 greenlet 实现 coroutine,创建.调度的开销比 线程(thread) 还小,因此程序内部的 执行流 效率高. gevent 实现了 ...

  6. Python 中 异步协程 的 使用方法介绍

    静觅 崔庆才的个人博客:Python中异步协程的使用方法介绍:https://cuiqingcai.com/6160.html Python 异步 IO .协程.asyncio.async/await ...

  7. Python并发之协程gevent基础

    基本示例 from gevent import monkey monkey.patch_all() # 记住一定放在第一行,这里是打补丁的意思,time模块在使用协程gevent模块的时候,必须打补丁 ...

  8. GO语言的进阶之路-协程和Channel

    GO语言的进阶之路-协程和Channel 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 看过我之前几篇博客小伙伴可能对Golang语言的语法上了解的差不多了,但是,如果想要你的代码 ...

  9. Python 异步,协程,学起来好头疼,Python爬虫程序能调用GPU去爬东西吗?

    78 技术人社群日报时间 文章目录 Python 爬虫程序能调用 GPU 去爬东西吗? Python 异步,协程--,学起来好头疼 有没有牛子大的说下 `matplotlib` 里 `plot` 和 ...

最新文章

  1. 参数化查询 但未提供该参数(将null插入数据库)
  2. 【收藏】批量导出docker镜像
  3. 【数据结构与算法】之深入解析“排列硬币”的求解思路与算法示例
  4. 前端学习(1917)vue之电商管理系统电商系统之绘制面包屑导航和卡片视图调用api获取数据
  5. mysql for update缺点_有关mysql的for update以及 死锁问题
  6. python爬虫:爬取某网站视频
  7. 西瓜书+实战+吴恩达机器学习(二二)概率图模型之马尔可夫随机场
  8. 2017IEC计算机第二次作业
  9. bootstrap checkbox_[推荐]icheck-bootstrap(漂亮的ckeckbox/radiobox)
  10. Makefile系列之五 :函数
  11. visio2010绘制思维导图方法
  12. airpods 升级固件版本_如何查看/更新AirPods的固件版本?AirPods升级查看固件新手教程...
  13. Mysql Workbench 8,连接时显示An AppArmor policy prevents this sender from sending this message to this rec
  14. 获取ua(user Agent)
  15. 原神抽卡逛街模拟器完整搭建教程-仅限单机
  16. 数据字典在web中的简单应用
  17. Windows10神州网信版的安装
  18. ubuntu18.04 安装迅雷 解决字体发虚与乱码问题
  19. Flappy bird 小游戏的实现
  20. Vs2019集成Teigha4.0

热门文章

  1. java redis pubsub_如何从Java中的生菜RedisPubSubListener获取消息?
  2. quickserver java_QuickServer--在吵闹的环境里快速搭建自己的TcpServer(Pragmatic系列) - java - CSDN技术中心...
  3. Python爬虫之(三)urllib库
  4. Linux-install-mysql5.6
  5. vue iview组件表格 render函数的使用
  6. JavaScript 常用数组函数方法专题
  7. 《Arduino开发实战指南:机器人卷》一2.2 模拟I/O口的操作函数
  8. Swift 反射 API 及用法
  9. [WebKit]浏览器的加载与页面性能优化
  10. linux 内核源码学习