반응형
발생 경위 : 비동기 함수 3개와 함께 다음과 같이 실행 시 발생하였음
async def crawl_and_insert(target: str, count: int, driver_class, source: str) -> None:
loop = asyncio.get_running_loop()
with ThreadPoolExecutor(max_workers=3) as executor:
# run_in_executor를 통해 크롤링을 비동기적으로 실행
data_list = await loop.run_in_executor(
executor, lambda: driver_class(target=target, count=count).news_collector()
)
if data_list: # 데이터가 있을 때만 처리
await mongo_main(data_list, source) # MongoDB에 데이터 삽입
async def crawling_data_insert_db(target: str, count: int):
tasks = [
crawl_and_insert(target, count, AsyncNaverNewsParsingDriver, "naver"),
crawl_and_insert(target, count, AsyncDaumNewsParsingDriver, "daum"),
crawl_and_insert(target, count, AsyncGoogleNewsParsingDriver, "google"),
]
await asyncio.gather(*tasks) # 모든 크롤링 작업을 동시에 수행
# 비동기 함수 실행
asyncio.run(crawling_data_insert_db("BTC", 3))
발생 이유: ThreadPoolExecutor 스레드에 이벤트 루프가 접근했을때 발생
스레드는 고유한 실행 환경을 가지고 있기 떄문에 asyncio 간 이벤트 루프가 스레드간 공유가 발생이 안됨
asyncio 비동기 기능을 사용하려고 할때, 현재 스레드에 이벤트 루프가 설정되어 있지 않다면 오류가 발생함
해결 방안
async def crawl_and_insert(target: str, count: int, driver: Callable, source: str) -> None:
loop = asyncio.get_running_loop()
def run_driver() -> UrlDictCollect:
# 새 이벤트 루프를 생성하고 실행
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
return new_loop.run_until_complete(driver(target=target, count=count).news_collector())
with ThreadPoolExecutor(max_workers=3) as executor:
# run_in_executor를 통해 크롤링을 비동기적으로 실행
data_list = await loop.run_in_executor(executor, run_driver)
if data_list: # 데이터가 있을 때만 처리
await mongo_main(data_list, source) # MongoDB에 데이터 삽입
loop.run_until_complete()는 비동기 코루틴이 완료될 때까지 이벤트 루프를 실행하므로,
호출한 스레드 내에서 해당 코루틴의 실행 순서는 보장
여러 스레드에서 이벤트 루프를 사용하는 경우에는 각 스레드가 독립적인 이벤트 루프를 가지고 있으므로,
스레드 간의 작업 실행 순서는 보장되지 않음
반응형
'오류모음집 > python' 카테고리의 다른 글
TypeError: 'ThreadPoolExecutor' object does not support the asynchronous context manager protocol (0) | 2024.10.11 |
---|---|
ImportError: attempted relative import with no known parent package (0) | 2023.05.02 |