python 中协程

python 中协程

异步非阻塞(I/O多路复用)
  1. I/O是指Input/Output(输入/输出), I/O多路复用是用于提升效率,单个进程通过一种机制可以同时监听多个网络连接IO,即监视多个文件描述符,一旦描述符就绪(读就绪和写就绪),能通知程序进行相应的读写操作。如进行多个网络请求时,可以通过非阻塞将请求全部发出,然后监听每个请求,如果发生变化,就直接进行下一步操作
  2. 常用的监听函数是select, 监听模式有select,和poll,以及epoll, r, w, e = select.select( rlist, wlist, errlist [,timeout] ),rlist,wlist和errlist均是文件描述符,就是一个整数,或者一个拥有返回文件描述符的函数fileno()的对象。rlist: 等待读就绪的文件描述符数组,wlist: 等待写就绪的文件描述符数组errlist: 等待异常的数组
协程(greenlet和gevent)
  1. 协程模块指的就是greenlet,但是greenlet模块实现不了单线程并发,只能通过greenlet+I/O切换实现单线程并发,gevent模块封装了此方法.
  2. 协程+I/O切换,只有遇到I/O密集型操作时才能实现单线程并发
  3. 协程与线程和进程不同,在计算机中是不存了的,是程序员创造出来的东西(微线程),用来实现执行过程中的切换,模块greenlet
import greenlet
def fun1():
    print(1)
    gr2.switch()#切换到协程gr2
    print(2)
    gr2.switch()
def fun2():
    print('a')
    gr1.switch()
    print('b')
gr1 = greenlet.greenlet(fun1)#创建一个协程
gr2 = greenlet.greenlet(fun2)#创建一个协程
gr1.switch()#执行协程gr1
  1. 单纯的协程是无用的,反而会影响执行效率,所以需要搭配异步非阻塞(I/O),遇到I/O就进行切换,这样才能实现单线程的并发
  2. greenlet+I/O多路复用,就是gevent实现遇到I/O自动执行切换,实现单线程并发,代码如下:
from gevent import monkey#
monkey.patch_all()#设置遇到I/O就切换
import requests,time
import gevent
start = time.time()
def GetHtml(url):
    '''
    获取源码的封装
    :param url: 传入url链接
    :return: 返回该连接对应源码
    '''
    try:  # 处理异常
        address = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36',
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"
      } #使用headers命令模仿浏览器向百度服务器发出请求
        r = requests.get(url,headers = address)
        r.encoding = r.apparent_encoding
        r.raise_for_status()
        html = r.text  # 获取到源码
        print(url,'\n',)
    except Exception as e:
        print(e)
gevent.joinall([
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=a"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=b"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=c"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=d"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=e"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=f"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=g"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=h"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=i"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=j"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=k"),
    gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=l"),
])
#gevent.spawn(GetHtml,"https://www.baidu.com/s?wd=l"),创建协程
#gevent.joinall([]),运行协程

# html1 =GetHtml("https://www.baidu.com/s?wd=a")
# html2 =GetHtml("https://www.baidu.com/s?wd=b")
# html3 =GetHtml("https://www.baidu.com/s?wd=c")
# html1 =GetHtml("https://www.baidu.com/s?wd=d")
# html2 =GetHtml("https://www.baidu.com/s?wd=e")
# html3 =GetHtml("https://www.baidu.com/s?wd=f")
# html1 =GetHtml("https://www.baidu.com/s?wd=g")
# html2 =GetHtml("https://www.baidu.com/s?wd=h")
# html3 =GetHtml("https://www.baidu.com/s?wd=i")
# html1 =GetHtml("https://www.baidu.com/s?wd=j")
# html2 =GetHtml("https://www.baidu.com/s?wd=k")
# html3 =GetHtml("https://www.baidu.com/s?wd=l")
end = time.time()
print('运行时间%.2f:' %(end-start))
#协程运行时间30.11:32.88:
#普通运行时间38.54:39.57
线程,进程,协程的区别
  1. 进程,资源分配的最小单元,操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源)
  2. 线程,工作的最小单元,是操作系统调度(CPU调度)执行的最小单位
  3. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;
  4. 资源分配给进程,同一进程的所有线程共享该进程的所有资源;
  5. 协程,人为定义的微线程协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
  6. 协程具有极高的执行效率,由于在一个线程中,不需要考虑变量,因为不存在同时写变量冲突,协程中也不需要考虑锁机制
  7. 常用的实现协程的模块gevent,以及基于事件循环的框架Twisted
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值