[英]One aiohttp ClientSession per thread?
在aiohttp.ClientSession
的文檔中,我已經讀到,理想情況下,您應該為每個應用程序創建一個ClientSession
。 我懷疑這應該讀作“為每個線程創建一個 ClientSession”(我在 python 3x 中)。
import aiohttp
import asyncio
async def get_it_async(url: str, session):
async with session.get(url) as resp:
print(resp.status)
print(len(await resp.text()))
def run_it(fn, *args, **kwargs):
# New thread - get the loop.
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
assert not loop.is_running()
return loop.run_until_complete(loop.create_task(fn(*args, **kwargs)))
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(2)
client = aiohttp.ClientSession()
await asyncio.wrap_future(pool.submit(run_it, get_it_async, 'httyp://nytimes.com', client))
給我:
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-1-180a408a9698> in async-def-wrapper()
~\appdata\Local\Programs\Python\Python37\lib\concurrent\futures\thread.py in run(self)
55
56 try:
---> 57 result = self.fn(*self.args, **self.kwargs)
58 except BaseException as exc:
59 self.future.set_exception(exc)
<ipython-input-1-180a408a9698> in run_it(fn, *args, **kwargs)
15
16
---> 17 from concurrent.futures import ThreadPoolExecutor
18 pool = ThreadPoolExecutor(2)
19
~\appdata\Local\Programs\Python\Python37\lib\asyncio\base_events.py in run_until_complete(self, future)
585 raise RuntimeError('Event loop stopped before Future completed.')
586
--> 587 return future.result()
588
589 def stop(self):
<ipython-input-1-180a408a9698> in get_it_async(url, session)
6 print(resp.status)
7 print(len(await resp.text()))
----> 8
9 def run_it(fn, *args, **kwargs):
10 # New thread - get the loop.
i:\gwatts\code\calratio2019\calms\.venv\lib\site-packages\aiohttp\client.py in __aenter__(self)
1010
1011 async def __aenter__(self) -> _RetType:
-> 1012 self._resp = await self._coro
1013 return self._resp
1014
i:\gwatts\code\calratio2019\calms\.venv\lib\site-packages\aiohttp\client.py in _request(self, method, str_or_url, params, data, json, cookies, headers, skip_auto_headers, auth, allow_redirects, max_redirects, compress, chunked, expect100, raise_for_status, read_until_eof, proxy, proxy_auth, timeout, verify_ssl, fingerprint, ssl_context, ssl, proxy_headers, trace_request_ctx)
424 timer = tm.timer()
425 try:
--> 426 with timer:
427 while True:
428 url, auth_from_url = strip_auth_from_url(url)
i:\gwatts\code\calratio2019\calms\.venv\lib\site-packages\aiohttp\helpers.py in __enter__(self)
577
578 if task is None:
--> 579 raise RuntimeError('Timeout context manager should be used '
580 'inside a task')
581
RuntimeError: Timeout context manager should be used inside a task
我的代碼中是否存在錯誤,或者我的假設是否正確?
我懷疑這應該讀作“為每個線程創建一個 ClientSession”(我在 python 3x 中)。
由於幾個原因,這種解釋是不正確的。 首先,建議的意思與書面完全一致 - 每個應用程序通常使用一個ClientSession
,其中應用程序是執行多個獨立(但松散相關)下載的程序。 例如,web 爬蟲是應用程序,bittorrent 客戶端也是如此。多個應用程序可以在單個進程中運行,在這種情況下,創建多個彼此完全獨立的會話是有意義的。
第二個問題是 asyncio 是單線程的,所以說“每個線程一個 session”是沒有意義的。 asyncio 的賣點之一是您可以實現不依賴於創建大量操作系統線程的可擴展並行性。 您的代碼創建一個線程來運行一個全新的事件只是為了下載一些東西在技術上是正確的,但沒有必要,特別是因為您的頂級代碼似乎一開始就在 asyncio 內部運行,它也可能很復雜且容易出錯,因為如果您曾經想在下載之間共享數據。 您將需要處理線程間同步問題。
在 asyncio 中實現並行性的方法是在您的進程中使用單個事件循環,並在其中執行所有操作。 例如,以您的get_it_async
function 作為起點,您可以這樣做以並行下載三個 URL:
async def main():
async with aiohttp.ClientSession() as session:
await asyncio.gather(
get_it_async('https://nytimes.com', session),
get_it_async('https://yahoo.com', session),
get_it_async('https://cnn.com', session),
)
# or, await main() if you call main from another async function
asyncio.run(main())
像asyncio.wrap_future
(更典型的是它的表親loop.run_in_executor
)這樣的函數應該只在從 asyncio 應用程序中調用 CPU-bound 或遺留阻塞代碼時使用。 如果您有異步代碼並從異步中調用它,只需等待它或使用asyncio.gather()
和類似方法使其並行運行。
據我所知, ClientSession()
和所有嵌套對象在實例化時保存循環 object (例如self._loop
)。 當您嘗試在另一個線程中使用它(因此是另一個活動的事件循環)時, ClientSession
內部代碼無法找到自己保存的循環的當前任務並引發此異常。
我認為對於每個新線程(以及每個新循環)都應該實例化新的ClientSession
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.