簡體   English   中英

每個線程一個 aiohttp ClientSession?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM