python协程使用场景_python之协程的那些事

python如何设置多进程(直通车)

协程

基本概念

协程,又称微线程,纤程。英文名Coroutine。协程是一种用户态的轻量级线程。

协程原理

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。线程的切换,会保存到CPU的寄存器里。 CPU感觉不到协程的存在,协程是用户自己控制的。之前通过yield做的生产者消费者模型,就是协程,在单线程下实现并发效果。

原理解读

协程原理:利用一个线程,分解一个线程成为多个“微线程”==>程序级别

如果写爬虫,就访问别的网站,拿别人源码。http请求叫IO请求,用多线程。

假设要访问3个url,创建3个线程,都在等待着,第一个有数据返回就继续执行,以此类推。

在等待过程中,就什么事也没干。

协程的方式。

计算机帮你创建进程、线程。线程是人为创建出来的。用一个线程,一会儿执行这个操作,一会儿执行那个操作。

协程是只用一个线程。程序员利用io多路复用的方式,让协程:

先访问一个url,不等待返回,就再访问第二个url,访问第三个url,然后也在等待。

greenlet本质是实现协程的。

注意:协程本身不高效,协程的本质只是程序员调用的,那为啥gevent这么高效率呢,是因为用了协程(greenlet)+IO多路复用的方式。

是IO多路复用的用法才能高效。所以用的时候就用gevent就好了。

#####协程的好处:

无需线程上下文切换的开销

无需数据操作锁定及同步的开销

方便切换控制流,简化编程模型

高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

缺点:

无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

应用场景:

IO密集型:用多线程+gevent(更好),多线程

计算密集型:用多进程

案例解读:

用多线程:假设每爬一个网址需要2秒,3个url,就是3个请求,等待2秒,就可以继续往下走。

如果用gevent,用单线程,单线程应该从上到下执行,用for循环读取3个url,往地址发送url请求,就是IO请求,线程是不等待的。

for循环再拿第二个url,再发第三个url。在这过程中,谁先回来,就处理谁。

资源占用上,多线程占用了3个线程,2秒钟,多线程啥也没干,在等待。gevent在2秒钟,只要发送请求了,接着就想干什么干什么。

案例:from urllib import requestimport gevent, time# 注意!:Gevent检测不到urllib的io操作,还是串行的,让它知道就需要打补丁from gevent import monkey

monkey.patch_all() # 把当前程序的所有IO操作单独的做上标记def f(url):

print("Get %s" %url)

resp = request.urlopen(url)

data = resp.read() # with open("url.html", 'wb') as f:

# f.write(data)

print("%d bytes received from %s" %(len(data), url))

print("异步时间统计中……") # 协程实现async_start_time = time.time()

gevent.joinall([

gevent.spawn(f, "https://www.python.org"),

gevent.spawn(f, "https://www.yahoo.com"),

gevent.spawn(f, "https://github.com"),

])

print("\033[32;1m异步cost:\033[0m",time.time()-async_start_time)#------------------------以下只为对比效果---------------------------print("同步步时间统计中……")

urls = [ "https://www.python.org", "https://www.yahoo.com", "https://github.com",

]

start_time = time.time()for url in urls:

f(url)

print("\033[32;1m同步cost:\033[0m",time.time()-start_time)123456789101112131415161718192021222324252627282930313233

小贴士:

gevent的使用场景举例:

1、scrapy框架内部用的gevent。发请求性能比线程高很多。

2、做api(url)监控,把代码发布到哪个url,得自动检测下返回值是不是200,或是指定的状态码。

发布完成之后,就要发送http请求过去检测一下返回的状态码。如果有20个url请求,就用gevent一下全给发了,就没必要创建多个线程,一个线程就足以了,然后配合多进程+gevent,又可以利用多颗cpu的优势了。

monkey.patch_all()是什么?

发送http请求,是request本质上调用socket来发。原来执行http请求,就会通知我一下,执行完了,默认socket是没有这个功能的。这相当于把原来的socket修改了,修改成特殊功能的socket,发送请求如果完事了,会告诉你完事了。

其实内部就是把io请求做了个封装而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值