非同期コンテキストマネージャとは、非同期メソッド __aenter__ および __aexit__ を定義し、async with 構文で使用されるオブジェクトです。このマネージャは、非同期関数におけるリソースの正しいオープン/クローズを行うために必要です:データベース接続、ファイル、セッションなど。
実装例:
class AsyncDBConnection: async def __aenter__(self): self.conn = await async_db_connect() return self.conn async def __aexit__(self, exc_type, exc, tb): await self.conn.close() async def main(): async with AsyncDBConnection() as conn: await conn.query('SELECT 1')
非同期コンテキストは、技術的な遅延によってイベントループがブロックされることを防ぎ、同時プログラムのパフォーマンスを向上させます。
async def 内で通常の with を使用することはできますか?
答え: はい、しかしコンテキストマネージャ内で操作が awaitable(await が必要)な場合、確実に async with が必要です。通常の with は非同期の enter/exit をサポートせず、イベントループがブロックされるか、enter/exit 内で await を呼び出すとエラーが発生します。
例:
async def foo(): with open('file.txt') as f: # 問題なし、同期的に読み取る data = f.read() # しかし通常のコンテキスト内で await はできません、async with 内のみ可能です。
事例
プロジェクト: 非同期APIを持つウェブサービス。
問題: データベース接続マネージャーが通常の with を使用していましたが、内部で await が呼び出されていました。これにより、RuntimeError("Cannot use 'await' outside async function") エラーが発生し、プロダクション環境でイベントループがブロックされました。
事例
プロジェクト: Websocketsを使用したチャット。
問題: 接続を扱う際に websocket リソースが閉じられず(通常の同期マネージャを使用)、メモリリークと接続のハング状態を引き起こしました。
事例
プロジェクト: マルチスレッド非同期ジョブキュー。
問題: タスクマネージャーが
__aexit__を正しく実装せず、awaitable を返すのを忘れていました。このため、タスクの終了が保証されず、一部のタスクがシステム内で「ハング」しました。