```
import asyncio
from logging import getLogger
from logging.config import dictConfig
from uuid import uuid4

import flet as ft
from PIL import Image

# 設定ファイルのインポート (実際のログ設定とパス定義はこれらのファイル内にあると仮定)
from configs.logging_config import LOGGING_CONFIG
from configs.path_config import EXAMPLE_IMAGE, OUTPUT_DIR
from utils.clipcrop import CropClip

# ログ設定の適用
dictConfig(LOGGING_CONFIG)
logger = getLogger(__name__)


class AsyncPipeline:
    """
    非同期タスク処理のためのパイプラインクラス。
    複数のワーカープロセスで画像処理タスクを並列実行し、結果をFutureで管理する。
    """

    def __init__(self, clip_processor_class, num_workers=1):
        """
        :param clip_processor_class: CropClipのような、withステートメントに対応した画像処理クラス
        :param num_workers: 同時に実行するワーカープロセスの数
        """
        self.clip_processor_class = clip_processor_class
        self.num_workers = num_workers
        self.queue = asyncio.Queue()
        # self.results ディクショナリは不要 (Futureオブジェクトが結果/例外を保持するため)
        # self.task_id_map も不要 (タスク名はコールバックに直接渡すため)
        self.futures = {}  # task_id -> asyncio.Future マッピング
        self.workers = []  # 非同期ワーカータスクのリスト

    async def _worker(self, name: str):
        """
        ワーカープロセスとして動作するコルーチン。
        キューからタスクを取得し、画像処理を実行して結果をFutureに設定する。
        """
        # withステートメントにより、画像処理クラスのリソース管理を行う
        with self.clip_processor_class() as processor:
            logger.info(f"Worker '{name}' initialized.")
            while True:
                try:
                    # キューからタスクデータを取得。タスクがない場合は待機。
                    task_data = await self.queue.get()

                    # シャットダウンシグナル (None) の場合、ループを抜けて終了
                    if task_data is None:
                        self.queue.task_done()
                        logger.info(f"Worker '{name}' received shutdown signal.")
                        break

                    task_id, img, prompt = task_data
                    logger.debug(f"Worker '{name}' processing task '{task_id[:6]}'")

                    try:
                        # ブロッキング処理である clip_clop を別スレッドで実行
                        result = await asyncio.to_thread(processor.clip_clop, img, prompt)

                        # 処理結果を対応するFutureオブジェクトに設定
                        self.futures[task_id].set_result(result)
                        logger.debug(f"Task '{task_id[:6]}' completed successfully by '{name}'.")
                    except Exception as e:
                        logger.exception(f"Worker '{name}' encountered exception for task '{task_id[:6]}'")
                        # エラーが発生した場合、例外をFutureオブジェクトに設定
                        self.futures[task_id].set_exception(e)
                    finally:
                        # キューのタスク完了を通知
                        self.queue.task_done()
                except asyncio.CancelledError:
                    # ワーカータスクがキャンセルされた場合、安全に終了
                    logger.info(f"Worker '{name}' was cancelled.")
                    break
                except Exception:
                    # queue.get() 等で予期せぬエラーが発生した場合
                    logger.exception(f"Unexpected error in worker '{name}'.")
                    break  # エラー発生時はワーカーを終了

    async def start_workers(self):
        """
        指定された数の非同期ワーカータスクを作成し、実行を開始する。
        """
        self.workers = [
            asyncio.create_task(self._worker(f"worker-{i}"))
            for i in range(self.num_workers)
        ]
        logger.info(f"{self.num_workers} workers started.")

    async def submit_task(self, name: str, prompt: str, img_path: str):
        """
        画像処理タスクをキューに追加し、その結果を待つためのFutureを返す。

        :param name: UI表示用などのタスク識別名
        :param prompt: CLIP処理用のプロンプト文字列
        :param img_path: 画像ファイルのパス
        :return: (task_id, future) タプル。task_idはタスクの一意な識別子、futureは処理結果を待つためのオブジェクト。
        """
        # 画像をメモリにロード
        try:
            with Image.open(img_path) as img:
                img.load()  # 画像データをメモリに読み込む
        except FileNotFoundError:
            error_msg = f"Image file not found: '{img_path}'"
            logger.error(error_msg)
            # 画像ロードエラー時もタスクIDとFutureを返し、UIでエラーを表示できるようにする
            task_id = str(uuid4())
            future = asyncio.get_running_loop().create_future()
            future.set_exception(FileNotFoundError(error_msg))
            # self.futures には追加しない（完了しないタスクとして扱わないため）
            return task_id, future
        except Exception as e:
            error_msg = f"Failed to load image '{img_path}': {e}"
            logger.error(error_msg)
            task_id = str(uuid4())
            future = asyncio.get_running_loop().create_future()
            future.set_exception(e)
            return task_id, future

        # タスクを一意に識別するIDを生成
        task_id = str(uuid4())

        # 結果を待つためのFutureオブジェクトを作成
        future = asyncio.get_running_loop().create_future()
        self.futures[task_id] = future  # タスク完了時の結果設定のために保持

        # タスクデータをキューに追加
        await self.queue.put((task_id, img, prompt))
        logger.debug(f"Task '{task_id[:6]}' submitted with prompt: '{prompt}'")

        return task_id, future

    # Fletアプリ終了時のクリーンアップ処理を明示的に行うメソッド。
    # ft.app_async の性質上、このメソッドが直接呼び出されるわけではないため、
    # イベントループ終了時にワーカーが適切に中断されることに依存する。
    async def stop(self):
        logger.info("Initiating pipeline stop...")
        # 全てのワーカーにシャットダウンシグナル (None) を送信
        for _ in self.workers:
            await self.queue.put(None)

        # キューが空になり、全てのタスクが処理完了するのを待機
        # 注意: このメソッドが呼び出されない場合、queue.join() は完了しない可能性がある。
        await self.queue.join()

        # 各ワーカータスクをキャンセル
        for w in self.workers:
            w.cancel()

        # キャンセルされたワーカータスクの終了を待機
        # return_exceptions=True で、タスク内の例外で gather が中断するのを防ぐ
        await asyncio.gather(*self.workers, return_exceptions=True)
        logger.info("AsyncPipeline workers stopped.")


# Flet UI の定義
async def main_ui_async(page: ft.Page):
    """
    Flet アプリケーションのメインUIを定義するコルーチン。
    非同期パイプラインを開始し、タスク投入ボタンとステータス表示リストを提供。
    """
    page.title = "Async Image Pipeline"
    page.vertical_alignment = ft.MainAxisAlignment.START  # 要素を上から配置

    # 処理ステータスを表示するリストビュー
    status_list = ft.ListView(
        expand=True,  # 利用可能なスペースをすべて使用
        spacing=5,  # コントロール間の間隔
        padding=10,  # リストの内側の余白
        auto_scroll=True  # 新しいメッセージが追加されたら自動スクロール
    )

    # AsyncPipeline インスタンスを作成 (ワーカー数: 2)
    pipeline = AsyncPipeline(CropClip, num_workers=2)

    # 非同期ワーカーを開始
    await pipeline.start_workers()

    # タスク実行ボタンがクリックされた際のイベントハンドラ
    async def handle_click(e):
        # 処理するタスクの定義 (名前, プロンプト, 画像パス)
        tasks_to_submit = {
            'girl': ('a girl', EXAMPLE_IMAGE),
            'clock': ('a clock on the wall', EXAMPLE_IMAGE),
            'teddy': ('a teddy bear', EXAMPLE_IMAGE),
        }

        # 各タスクをループしてパイプラインに投入
        for name, (prompt, img_path) in tasks_to_submit.items():
            # タスクをパイプラインにサブミットし、タスクIDとFutureを取得
            task_id, future = await pipeline.submit_task(name, prompt, img_path)

            # UIにタスク投入メッセージを追加
            status_list.controls.append(ft.Text(f"➡️ Submitted task '{name}' ({task_id[:6]})."))
            page.update()  # UIを更新してメッセージを表示

            # タスクの結果を非同期で待機し、UIを更新するコルーチン
            async def await_and_display_result(current_task_id, task_name, task_future):
                try:
                    # Future が完了するのを待つ
                    result = await task_future

                    # 結果がPIL Image オブジェクトの場合
                    if isinstance(result, Image.Image):
                        # 出力ファイルパスを生成
                        out_path = OUTPUT_DIR / f"ui_output_{task_name}_{current_task_id[:6]}.png"
                        # 画像を保存
                        result.save(out_path)
                        # UIに成功メッセージを追加
                        status_list.controls.append(
                            ft.Text(f"✅ Task '{task_name}' ({current_task_id[:6]}) completed. Saved to {out_path}"))
                    else:
                        # 結果の型が予期しない場合
                        status_list.controls.append(ft.Text(
                            f"⚠️ Task '{task_name}' ({current_task_id[:6]}) finished with unexpected result type: {type(result)}"))

                except FileNotFoundError as e:
                    # 画像読み込み失敗時のエラー表示
                    status_list.controls.append(ft.Text(f"❌ Task '{task_name}' ({current_task_id[:6]}) failed: {e}"))
                except Exception as e:
                    # CLIP処理中の例外など、その他のエラー表示
                    status_list.controls.append(ft.Text(f"❌ Task '{task_name}' ({current_task_id[:6]}) failed: {e}"))
                finally:
                    # 結果表示後にUIを更新
                    page.update()

            # 結果表示のためのコルーチンを非同期タスクとして作成・実行
            # このタスクはUIスレッドをブロックせず、バックグラウンドで実行される
            page.run_task(await_and_display_result, task_id, name, future)

    # タスク実行を開始するボタン
    run_button = ft.ElevatedButton("Run Tasks", on_click=handle_click)

    # UI要素をページに追加
    page.add(
        ft.Column(
            [
                run_button,
                ft.Divider(),  # 区切り線
                ft.Text("Processing Status:", weight=ft.FontWeight.BOLD),
                status_list  # ステータス表示リストを追加
            ],
            expand=True  # カラムが利用可能なスペースをすべて使うように設定
        )
    )


async def main():
    """
    Flet アプリケーションを非同期で起動するメインエントリーポイント。
    """
    # 重要: Flet の非同期アプリケーションを実行するには ft.app_async を使用します。
    await ft.app_async(target=main_ui_async)


if __name__ == "__main__":
    # スクリプトが直接実行された場合のメイン処理
    asyncio.run(main())
```

threadripper 2990wxを使用しているけど、このコードでCPU使用率が100%になる。なので、キューの概念は維持しつつ、ワーカー的なものはどんな理由があろうと１個で固定する。キューを残してワーカー的なものをなくしたほうがよい？ワーカーを１つにしたほうがよい？