Event用计算机语言,[gevent并发编程] 1. 为什么要使用gevent?

前言

关于并发,原生语义指的是计算机(具有多核CPU) 能够利用多线程特性同时执行一批任务,但注意,“同时”区别于“同一时刻”。

就好比打鼹鼠游戏,你一个人玩的话,就是单核并发,你同时盯住多个洞口等待鼹鼠(任务)出现,但同一时刻只能击打一只鼹鼠(只有一个CPU核心在干活),当鼹鼠(待执行的任务量)变多了,你的击中率(计算机的执行效率)自然就会变低。

单核并发总结:单核并发并不能提高效率,反而是降低效率。

多核并发更高级,指的是计算机能够有效利用多核CPU,能够有效提高并发任务的执行效率(但仍然是利用多线程)。复用上面的例子,这次给你加个队友(单核变双核),两个人一起盯住多个洞口,在鼹鼠出现次数相同的情况下,击中率明显提高。

并发总结:增加CPU核心可以提高计算机并发性能,但仍然存在一个问题:单个CPU核心都是通过不停的切换执行的方式进行的,好比人脑同时思考多个事情,人脑会累事情多了会切换回变慢进而导致整个人的思考效率变慢;计算机并发机制类似,切换任务时需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行,这是确实存在的开销,且随任务量的增加而增加。(你可以写一段代码来测试,比如单线程计算一个数学表达式,然后1W个线程计算1W次,将并均值与单线程耗时进行对比)

并行,原生语义指的是计算机利用多进程特性能够同一时刻运行多个任务,这是效率最高的任务执行模型。仍然复用上面的例子,两个人,每个只需要各自盯住一个洞口,在鼹鼠出现次数相同的情况下,这种方式的击中率显然要最高于并发的,因为没有切换开销。不过,并行任务量一般受限于CPU的核心数。

并行+并发,现代网络服务流行的任务执行模型。

并行+异步,现在网络服务中性能最高的任务执行模型,Nginx就是采用这种模型。

Python中的并发

GIL是Python的命门,它使得Cpython解释器解释的Python代码执行时在同一时刻永远只能有一个线程在执行(这里特指是在多核CPU环境下),为什么有GIL,官方解释:

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once.?This lock is necessary mainly because CPython’s memory management is not thread-safe.?(However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

大意是Cpython的内存管理不是线程安全的。

也就是说,在Python中(指使用Cpython解释的代码程序),即使是多核计算机,也只有单核并发一种执行模型(暂不讨论并行),因为这个原因,在Python中多线程访问一个全局变量不加锁也不会有异常抛出,且程序也会正确执行。

单核并发并不是完全毫无用武之地!

Python常用来进行数据采集,数据采集是IO密集型的任务,而Python多线程虽然是单核并发,但是,在执行的代码正在进行IO操作时,此刻会暂时释放GIL锁,等IO完成时再获取GIL锁执行后续代码。IO密集型的任务意味着程序更多的耗时在网络IO上,如果能够降低程序在这方面的耗时,那就提高了程序的性能,能够达到我们的目的。

试试执行这段代码:

import requests,threading,time

def func(seq):

print('send req-%s' % seq)

r = requests.get('https://csdn.net')

print('ret code:',r.status_code)

ths = []

time_st = int(time.time())

for i in range(10):

t = threading.Thread(target=func, args=(i,))

t.setDaemon(True)

t.start()

ths.append(t)

for t in ths:

t.join() # 等待任务完成

print('time cost %s secs' % (int(time.time())-time_st))

在我的PC上执行耗时1s,你可以再计算按顺序执行10个HTTP req的耗时,再与之对比,观察。

前面说了,线程切换的开销的确存在,那我们能不能把这部分开销也避免掉呢? 答案是肯定的,可以使用Python中的协程。

协程(coroutine)概念的出现晚于进程和线程,但相对而言更加灵活。协程用于协作式的多任务模型。

对比线程,它们都有自己的上下文,但线程的切换由操作系统控制,协程则是由开发者控制。还有一个特性,协程是协作式的,这个特性使得在协程中访问共享资源也不需要加锁,这就把上下文切换的这部分开销给避免了。

协作式(cooperative)的任务执行模型,体验在程序中,就是多个任务之间的切换是协作式的,并不是由OS进行强行切换,而是开发者控制切换的时机,例如,在一个函数中,执行到某一个步时(开始从网络下载数据时),我想要把CPU控制权让出去,暂时去执行别的函数,让这个函数先下载数据,等它下载完的时候再回来执行。重点是我可以手动控制,什么时候切换出去,当然不只是IO任务才能切换了,通过sleep(0)也可以,这就实现了协作式的任务执行模型。

协程有几个典型的接口,create/resume/running/status/yield,其中yield我们看起来比较熟悉,在Python中,yield是一个关键字,它一定程度上实现了协程。代码示例可以参考

Python中的协程

上面提到了Python中协程可以用yield关键字来实现,但实现的功能确实太局限了。通过廖雪峰的例子我们只能看到协程是什么时候切换的,但使用也是很不方便的,按照Lua的协程实现来看,语言对于协程的支持,应该具有以下API:?

coroutine.yield()? ?//切换出去

coroutine.status()? // 状态查询

coroutine.wrap()? ?// 函数封装

coroutine.start()? ?// 启动

coroutine.kill()? ? ? // 杀死

coroutine.result()? ? // 结果获取

coroutine.exception()? // 异常获取

有了这些API,在程序中使用协程就更方便了。

所以,在Python3.4 出现了一个asyncio的异步框架库,它提供了一系列高级API,比如协程的所有api,子进程控制,协程同步API,经过好几个版本的迭代,Python3.7中的asyncio已经比较成熟,如果你想深入学习asyncio,

为什么在这里我不推荐asyncio?四点如下:

概念复杂,asyncio库内部增加了Future,Task,Event-loop,Transports等等一系列概念,官方文档不下几十万字(words),而且要求我们必须学习得足够熟练,才能够安全的在日常开发中使用,上手成本剧增。(asyncio文档编写成一本书不足为奇)

掌握asyncio还不够,你还必须掌握如何写异步代码,它在PEP 492中提出,具体来说就是写非阻塞式的代码,而这个文档也是有一定学习成本的。你如果认为定义一个普通函数,然后使用@asyncio.coroutine装饰一下,它就真的异步执行了吗,不是的,你可以试试执行10个coroutine,每个coroutine都是请求一个url,然后asyncio.run一下看看有没有IO并发,像多线程那样。不过有牛人已经学会了这个语法,进而开发出异步网络库了。比如aiohttp, aioredis,掌握这些库的使用你也可以写异步代码。

如果在代码中使用asyncio,这意味着你的大部分代码几乎都是异步的,都会用到asyncio的接口。如果是你一个人维护这份代码,那没问题。如果是多人维护的话,这可能会成为一个最令你头疼的问题,因为人才市场上真正掌握asyncio和async语法的人少之又少。你必须要在招聘需求注明"熟悉协程,掌握asyncio使用以及async语法"。如果只懂几个简单的api调用,你不太可能会聘用他。

我比较懒。

gevent

铺垫了这么多,重要人物终于要上场了,文字较多,此小节转到第二篇文章介绍,敬请期待。

?

本文由个人总结而成,如有瑕疵或错误,请读者留言。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值