方式
- 多线程、多进程:不推荐
- 好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行。
- 坏处:无法无限制的开启多线程或者多进程。
- 线程池、进程池:适当使用
- 好处:可以降低系统对进程或者线程创建和销毁的一个频率,从而很好的降低系统的开销。
- 坏处:池中线程数量有上限。
- 单线程+异步协程:推荐
- event_loop:事件循环,相当于一个无限循环,可以把一些函数注册到这个事件循环上,当满足条件时,函数会被循环执行。
- coroutine:协程对象,可以将协程对象注册到事件循环中,它会被事件循环调用。
可以使用async
关键字来定义一个方法,这个方法在调用时不会被立即执行,而是返回一个协程对象。 - task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。
- future:代表将来执行或还没有执行的任务,实际上和
task
没有本质上的区别。 - async:定义一个协程
- await:用来挂起阻塞方法的执行
多线程好处
使用异步实现高性能的数据爬取操作(人多力量大)
多任务异步协程
# 环境安装:pip install aiohttp
# 使用该模块中的ClientSession
import requests
import asyncio
import time
import aiohttp
from multiprocessing.dummy import Pool
start = time.time()
# 2表示同时存在两个协程
pool = Pool(2)
urls = []
for i in range(10):
urls.append('http://127.0.0.1:5000/bobo')
print(urls)# 生成一组请求URL,每个都会被阻塞2s
# 使用async新建一个协程
async def get_page(url):
async with aiohttp.ClientSession() as session:
#get()、post():
#headers,params/data,proxy='http://ip:port'
async with await session.get(url) as response:
#text()返回字符串形式的响应数据
#read()返回的二进制形式的响应数据
#json()返回的就是json对象
#注意:获取响应数据操作之前一定要使用await进行手动挂起
page_text = await response.text()
print(page_text)
tasks = []
for url in urls:
c = get_page(url)
task = asyncio.ensure_future(c)
tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print('总耗时:',end-start)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
线程与协程的区别
-
一个线程可以多个协程,一个进程也可以单独拥有多个协程。
-
线程进程都是同步机制,而协程则是异步。
-
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
-
线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
-
协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器可以关联任意线程或线程池, 可以使当前线程, UI线程, 或新建新程.。
-
线程是协程的资源。协程通过Interceptor来间接使用线程这个资源。