python由于GIL(全局锁)的存在无法发挥多核的优势。在IO密集型网络编程中,异步处理相对于同步处理可以提高成百上千倍的效率。弥补了python性能的不足。python3.4版本在标准库中引入了asyncio,python2x没有加入这个库,python3.5加入了async/await特性。同步/异步的概念Synchronization是指完成事务的逻辑。第一个事务首先执行。如果是阻塞的,会一直等到事务完成,然后依次执行第二个事务。异步与同步相反。异步是指在处理调用本次事务后,不等待本次事务的处理结果,直接处理第二个事务,并通过状态、通知、回调等方式将处理结果通知给调用方。asyncio比较了同步代码和异步代码写法的区别,再来看两者的性能差距,使用sleep(1)来模拟耗时1秒的io操作。同步代码导入timedefhello():time.sleep(1)defrun():foriinrange(5):hello()print(time.time())if__name__=='__main__':run()#结果##1S#1549956123.90849#1549956124.9128718#1549956125.9164648#1549956126.91946))defrun():foriinrange(5):loop.run_until_complete(hello())loop=asyncio.get_event_loop()if__name__=='__main__':run()#Result#1549956258.2929049#1549956258.293014#1549956258.293063#1549956258.2931032#1549956258.293142#asyncdef用来定义异步函数,里面有异步操作。每个线程都有一个事件循环,主线程在调用asyncio.get_event_loop()时会创建一个事件循环,#需要把异步任务丢给这个循环的run_until_complete()方法,事件循环会安排执行协程的。aiohttp需要并发http请求怎么办?通常使用requests,但是requests是一个同步库。如果要异步,需要引入aiohttp。这里引入一个类,fromaiohttpimportClientSession。首先,必须创建一个会话对象,然后使用会话对象打开网页。Session可以进行多种操作,如post、get、put、head等。基本用法asyncwithClientSession()assession:asyncwithsession.get(url='http://www.baidu.com/')asresponse:首先asyncdef关键字定义这是一个异步函数,而await关键字在需要等待的操作之前加上response.read()等待请求响应,这是一个很好的IO操作。然后使用ClientSession类发起http请求。多链接异步访问需要请求多个url怎么办?要同步访问多个URL,我们只需要添加一个for循环。但是异步实现就没那么容易了。基于前面的基础,需要将hello()包裹在asyncio的Future对象中,然后将Future对象的列表作为任务传递给事件循环。#!/usr/bin/envpython#-*-编码:utf-8-*-importasynciofromaiohttpimportClientSessiontasks=[]asyncdefrequest_run(i):asyncwithClientSession()assession:asyncwithsession.get(url='http://www.baidu.com/')asresponse:response=awaitresponse.read()#返回消息print(i)defrun():foriinrange(5):task=asyncio.ensure_future(request_run(i))tasks.append(task)if__name__=='__main__':loop=asyncio.get_event_loop()run()loop.run_until_complete(asyncio.wait(tasks))#Output#0#1#3#4#2收集http响应,需要将响应一个一个收集成一个列表,最后保存到本地或者打印出来。如何做到这一点?您可以通过asyncio收集所有响应。具体的,下面用例子来说明。#!/usr/bin/envpython#-*-编码:utf-8-*-importasynciofromaiohttpimportClientSessiontasks=[]asyncdefrequest_run(i):asyncwithClientSession()assession:asyncwithsession.get(url='http://www.baidu.com/')作为响应:response=awaitresponse.read()print(i)print(response)returnresponsedefrun():foriinrange(5):task=asyncio.ensure_future(request_run(i))tasks.append(task)result=loop.run_until_complete(asyncio.gather(*tasks))print(result)if__name__=='__main__':loop=asyncio.get_event_loop()run()AboutValueError:toomanyfiledescriptorsinselect()异常解决办法如果你的并发数达到2000,程序会报错:ValueError:toomanyfiledescriptorsinselect()。错误的原因从字面上看是Python调用的select有一个打开文件的最大数量。这实际上是操作系统的限制。linux默认最大打开文件数是1024,windows默认是509,超过了这个值。程序开始报错。这里我们有三种方法来解决这个问题:1.限制并发数。(不要一次塞那么多任务,或者限制最大并发数)2.使用回调方法。3.修改操作系统打开文件数的最大限制。系统中有一个配置文件可以修改默认值。具体步骤不再赘述。如果不修改系统默认配置,我个人比较推荐限制并发数的方法,设置并发数为500,处理速度更快。#!/usr/bin/envpython#-*-coding:utf-8-*-importasyncioimporttimefromaiohttpimportClientSessiontasks=[]a=time.time()asyncdefrequest_run(i,semaphore):与信号量异步:asyncwithClientSession()assession:asyncwithsession.get(url='https://segmentfault.com/q/1010000011211509')asresponse:response=awaitresponse.read()tasks.append(i)#可以是storedReturnresultasyncdefrun():semaphore=asyncio.Semaphore(500)to_get=[request_run(i,semaphore)foriinrange(1000)]#总共1000个任务等待asyncio.wait(to_get)if__name__=='__main__':loop=asyncio.get_event_loop()loop.run_until_complete(run())loop.close()print(tasks)b=time.time()print(b-a)来源网络,仅供学习,如有侵权,请联系删除。学习Python的路上肯定会遇到困难,不要慌张,我这里有一套学习资料,包括40+电子书,800+教学视频,涉及Python基础、爬虫、框架、数据分析、机学习等等,别怕你学不会!https://shimo.im/docs/JWCghr8...《Python学习资料》关注公众号【蟒圈】,每日优质文章推送。
