V2EX › Python
Python 中的 asyncio+aiohttp 为什么还要创建线程
zl2003cn · 2017-02-21 11:11:43 +08:00 · 4092 次点击
这是一个创建于 1387 天前的主题,其中的信息可能已经有所发展或是发生改变。
asyncio 不是用协程吗?我理解协程就应该是一个进程只创建一个线程,由这个线程自行调度协程,而使用 asyncio+aiohttp 时创建了 25 个线程, gevent+requests 创建了 15 个线程,这又是怎么理解?协程不就是为了节省线程切换带来的性能损耗吗?
第 1 条附言 · 2017-02-21 13:46:12 +08:00
modified fetch function with semaphore
import random
import asyncio
from aiohttp import ClientSession
async def fetch(url, session):
async with session.get(url) as response:
delay = response.headers.get("DELAY")
date = response.headers.get("DATE")
print("{}:{} with delay {}".format(date, response.url, delay))
return await response.read()
async def bound_fetch(sem, url, session):
# Getter function with semaphore.
async with sem:
await fetch(url, session)
我在Windows上跑,在资源管理器看到创建了25个线程
async def run(r):
url = "http://localhost:8080/{}"
tasks = []
# create instance of Semaphore
sem = asyncio.Semaphore(1000)
# Create client session that will ensure we dont open new connection
# per each request.
async with ClientSession() as session:
for i in range(r):
# pass Semaphore and session to every GET request
task = asyncio.ensure_future(bound_fetch(sem, url.format(i), session))
tasks.append(task)
responses = asyncio.gather(*tasks)
await responses
number = 10000
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(number))
loop.run_until_complete(future)
第 2 条附言 · 2017-02-21 13:47:39 +08:00
modified fetch function with semaphore
import random
import asyncio
from aiohttp import ClientSession
async def fetch(url, session):
async with session.get(url) as response:
delay = response.headers.get("DELAY")
date = response.headers.get("DATE")
print("{}:{} with delay {}".format(date, response.url, delay))
return await response.read()
async def bound_fetch(sem, url, session):
# Getter function with semaphore.
async with sem:
await fetch(url, session)
async def run(r):
url = "http://localhost:8080/{}"
tasks = []
# create instance of Semaphore
sem = asyncio.Semaphore(1000)
# Create client session that will ensure we dont open new connection
# per each request.
async with ClientSession() as session:
for i in range(r):
# pass Semaphore and session to every GET request
task = asyncio.ensure_future(bound_fetch(sem, url.format(i), session))
tasks.append(task)
responses = asyncio.gather(*tasks)
await responses
number = 10000
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(number))
loop.run_until_complete(future)
我在Windows的资源管理器看的线程数,刚才中间怎么打断了。。。
第 3 条附言 · 2017-02-21 14:31:32 +08:00
gevent:
import gevent.monkey
gevent.monkey.patch_socket()
import gevent
import urllib.request as req
import json
def fetch(pid):
response = req.urlopen('http://localhost:8080/')
result = response.read()
#json_result = json.loads(result)
#datetime = json_result['datetime']
print('Process %s' % (pid))
return result
def asynchronous():
threads = []
for i in range(1,800):
threads.append(gevent.spawn(fetch, i))
gevent.joinall(threads)
print('Asynchronous:')
asynchronous()
gevent打了monkey patch也是创建15线程呀。。
第 4 条附言 · 2017-02-21 19:03:24 +08:00
自己结帖,测试数据为:Windows下用asyncio跑一个run_forever()要开5个线程,用aiohttp发起请求最大创建25个线程,使用基于线程池的DNS resolver的gevent发起请求创建15个线程。而Linux(Ubuntu)下用asyncio跑空的事件循环只用1个线程,用aiohttp发起请求也只用1个线程,使用基于线程池DNS resolver的gevent创建10个线程,而在设置export GEVENT_RESOLVER=ares后也是只开一个线程,在Linux下的行为符合我对基于协程的异步IO的理解,而Windows下ayncio库为什么要创建这么多线程还是不理解,google也搜不到什么信息,望有大神告知。
第 5 条附言 · 2017-02-21 19:09:16 +08:00
不对,刚再测了一下aiohttp发起请求还是要创建5个线程的,但也比Windows下创建少的多。。