一、同步与异步
#同步编程(同一时间只能做一件事,做完了才能做下一件事情) <-a_url-><-b_url-><-c_url-> #异步编程 (可以近似的理解成同一时间有多个事情在做,但有先后) <-a_url-> <-b_url-> <-c_url-> <-d_url-> <-e_url-> <-f_url-> <-g_url-> <-h_url-> <--i_url--> <--j_url-->
模板
import asyncio #函数名:做现在的任务时不等待,能继续做别的任务。 async def donow_meantime_dontwait(url): response = await requests.get(url) #函数名:快速高效的做任务 async def fast_do_your_thing(): await asyncio.wait([donow_meantime_dontwait(url) for url in urls]) #下面两行都是套路,记住就好 loop = asyncio.get_event_loop() loop.run_until_complete(fast_do_your_thing())
tips:
- await表达式中的对象必须是awaitable
- requests不支持非阻塞
- aiohttp是用于异步请求的库
代码
import asyncio import requests import time import aiohttp urls = ['https://book.douban.com/tag/小说','https://book.douban.com/tag/科幻', 'https://book.douban.com/tag/漫画','https://book.douban.com/tag/奇幻', 'https://book.douban.com/tag/历史','https://book.douban.com/tag/经济学'] async def requests_meantime_dont_wait(url): print(url) async with aiohttp.ClientSession() as session: async with session.get(url) as resp: print(resp.status) print("{url} 得到响应".format(url=url)) async def fast_requsts(urls): start = time.time() await asyncio.wait([requests_meantime_dont_wait(url) for url in urls]) end = time.time() print("Complete in {} seconds".format(end - start)) loop = asyncio.get_event_loop() loop.run_until_complete(fast_requsts(urls))
gevent简介
gevent是一个python的并发库,它为各种并发和网络相关的任务提供了整洁的API。
gevent中用到的主要模式是greenlet,它是以C扩展模块形式接入Python的轻量级协程。 greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
猴子补丁
requests库是阻塞式的,为了将requests同步更改为异步。只有将requests库阻塞式更改为非阻塞,异步操作才能实现。
而gevent库中的猴子补丁(monkey patch),gevent能够修改标准库里面大部分的阻塞式系统调用。这样在不改变原有代码的情况下,将应用的阻塞式方法,变成协程式的(异步)。
代码
from gevent import monkey import gevent import requests import time monkey.patch_all() def req(url): print(url) resp = requests.get(url) print(resp.status_code,url) def synchronous_times(urls): """同步请求运行时间""" start = time.time() for url in urls: req(url) end = time.time() print('同步执行时间 {} s'.format(end-start)) def asynchronous_times(urls): """异步请求运行时间""" start = time.time() gevent.joinall([gevent.spawn(req,url) for url in urls]) end = time.time() print('异步执行时间 {} s'.format(end - start)) urls = ['https://book.douban.com/tag/小说','https://book.douban.com/tag/科幻', 'https://book.douban.com/tag/漫画','https://book.douban.com/tag/奇幻', 'https://book.douban.com/tag/历史','https://book.douban.com/tag/经济学'] synchronous_times(urls) asynchronous_times(urls)
gevent:异步理论与实战
gevent库中使用的最核心的是Greenlet-一种用C写的轻量级python模块。在任意时间,系统只能允许一个Greenlet处于运行状态
一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
串行和异步
高并发的核心是让一个大的任务分成一批子任务,并且子任务会被被系统高效率的调度,实现同步或者异步。在两个子任务之间切换,也就是经常说到的上下文切换。
同步就是让子任务串行,而异步有点影分身之术,但在任意时间点,真身只有一个,子任务并不是真正的并行,而是充分利用了碎片化的时间,让程序不要浪费在等待上。这就是异步,效率杠杆的。
gevent中的上下文切换是通过yield实现。在这个例子中,我们会有两个子任务,互相利用对方等待的时间做自己的事情。这里我们使用gevent.sleep(0)代表程序会在这里停0秒。
import gevent def foo(): print('Running in foo') gevent.sleep(0) print('Explicit context switch to foo again') def bar(): print('Explicit context to bar') gevent.sleep(0) print('Implicit context switch back to bar') gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar) ])
运行的顺序:
Running in foo Explicit context to bar Explicit context switch to foo again Implicit context switch back to bar
同步异步的顺序问题
同步运行就是串行,123456...,但是异步的顺序是随机的任意的(根据子任务消耗的时间而定)
代码
import gevent import random def task(pid): """ Some non-deterministic task """ gevent.sleep(random.randint(0,2)*0.001) print('Task %s done' % pid) #同步(结果更像串行) def synchronous(): for i in range(1,10): task(i) #异步(结果更像乱步) def asynchronous(): threads = [gevent.spawn(task, i) for i in range(10)] gevent.joinall(threads) print('Synchronous同步:') synchronous() print('Asynchronous异步:') asynchronous()
输出
Synchronous同步: Task 1 done Task 2 done Task 3 done Task 4 done Task 5 done Task 6 done Task 7 done Task 8 done Task 9 done Asynchronous异步: Task 1 done Task 5 done Task 6 done Task 2 done Task 4 done Task 7 done Task 8 done Task 9 done Task 0 done Task 3 done
同步案例中所有的任务都是按照顺序执行,这导致主程序是阻塞式的(阻塞会暂停主程序的执行)。
gevent.spawn会对传入的任务(子任务集合)进行进行调度,gevent.joinall方法会阻塞当前程序,除非所有的greenlet都执行完毕,程序才会结束。
实战
实现gevent到底怎么用,把异步访问得到的数据提取出来。
在有道词典搜索框输入“hello”按回车。观察数据请求情况 观察有道的url构建。
分析url规律
#url构建只需要传入word即可 url = "http://dict.youdao.com/w/eng/{}/".format(word)
解析网页数据
def fetch_word_info(word): url = "http://dict.youdao.com/w/eng/{}/".format(word) resp = requests.get(url,headers=headers) doc = pq(resp.text) pros = '' for pro in doc.items('.baav .pronounce'): pros+=pro.text() description = '' for li in doc.items('#phrsListTab .trans-container ul li'): description +=li.text() return {'word':word,'音标':pros,'注释':description}
因为requests库在任何时候只允许有一个访问结束完全结束后,才能进行下一次访问。无法通过正规途径拓展成异步,因此这里使用了monkey补丁
同步代码
import requests from pyquery import PyQuery as pq import gevent import time import gevent.monkey gevent.monkey.patch_all() words = ['good','bad','cool', 'hot','nice','better', 'head','up','down', 'right','left','east'] def synchronous(): start = time.time() print('同步开始了') for word in words: print(fetch_word_info(word)) end = time.time() print("同步运行时间: %s 秒" % str(end - start)) #执行同步 synchronous()
异步代码
import requests from pyquery import PyQuery as pq import gevent import time import gevent.monkey gevent.monkey.patch_all() words = ['good','bad','cool', 'hot','nice','better', 'head','up','down', 'right','left','east'] def asynchronous(): start = time.time() print('异步开始了') events = [gevent.spawn(fetch_word_info,word) for word in words] wordinfos = gevent.joinall(events) for wordinfo in wordinfos: #获取到数据get方法 print(wordinfo.get()) end = time.time() print("异步运行时间: %s 秒"%str(end-start)) #执行异步 asynchronous()
我们可以对待爬网站实时异步访问,速度会大大提高。我们现在是爬取12个词语的信息,也就是说一瞬间我们对网站访问了12次,这还没啥问题,假如爬10000+个词语,使用gevent的话,那几秒钟之内就给网站一股脑的发请求,说不定网站就把爬虫封了。
解决办法
将列表等分为若干个子列表,分批爬取。举例我们有一个数字列表(0-19),要均匀的等分为4份,也就是子列表有5个数。下面是我在stackoverflow查找到的列表等分方案:
方法1
seqence = list(range(20)) size = 5 #子列表长度 output = [seqence[i:i+size] for i in range(0, len(seqence), size)] print(output)
方法2
chunks = lambda seq, size: [seq[i: i+size] for i in range(0, len(seq), size)] print(chunks(seq, 5))
方法3
def chunks(seq,size): for i in range(0,len(seq), size): yield seq[i:i+size] prinT(chunks(seq,5)) for x in chunks(req,5): print(x)
数据量不大的情况下,选哪一种方法都可以。如果特别大,建议使用方法3.
动手实现
import requests from pyquery import PyQuery as pq import gevent import time import gevent.monkey gevent.monkey.patch_all() words = ['good','bad','cool', 'hot','nice','better', 'head','up','down', 'right','left','east'] def fetch_word_info(word): url = "http://dict.youdao.com/w/eng/{}/".format(word) resp = requests.get(url,headers=headers) doc = pq(resp.text) pros = '' for pro in doc.items('.baav .pronounce'): pros+=pro.text() description = '' for li in doc.items('#phrsListTab .trans-container ul li'): description +=li.text() return {'word':word,'音标':pros,'注释':description} def asynchronous(words): start = time.time() print('异步开始了') chunks = lambda seq, size: [seq[i: i + size] for i in range(0, len(seq), size)] for subwords in chunks(words,3): events = [gevent.spawn(fetch_word_info, word) for word in subwords] wordinfos = gevent.joinall(events) for wordinfo in wordinfos: # 获取到数据get方法 print(wordinfo.get()) time.sleep(1) end = time.time() print("异步运行时间: %s 秒" % str(end - start)) asynchronous(words)
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 【雨果唱片】中国管弦乐《鹿回头》WAV
- APM亚流新世代《一起冒险》[FLAC/分轨][106.77MB]
- 崔健《飞狗》律冻文化[WAV+CUE][1.1G]
- 罗志祥《舞状元 (Explicit)》[320K/MP3][66.77MB]
- 尤雅.1997-幽雅精粹2CD【南方】【WAV+CUE】
- 张惠妹.2007-STAR(引进版)【EMI百代】【WAV+CUE】
- 群星.2008-LOVE情歌集VOL.8【正东】【WAV+CUE】
- 罗志祥《舞状元 (Explicit)》[FLAC/分轨][360.76MB]
- Tank《我不伟大,至少我能改变我。》[320K/MP3][160.41MB]
- Tank《我不伟大,至少我能改变我。》[FLAC/分轨][236.89MB]
- CD圣经推荐-夏韶声《谙2》SACD-ISO
- 钟镇涛-《百分百钟镇涛》首批限量版SACD-ISO
- 群星《继续微笑致敬许冠杰》[低速原抓WAV+CUE]
- 潘秀琼.2003-国语难忘金曲珍藏集【皇星全音】【WAV+CUE】
- 林东松.1997-2039玫瑰事件【宝丽金】【WAV+CUE】