mainloopのスレッド管理を改善し、マルチワーカー化を実装。デバイス管理の初期化を遅延させ、エラーハンドリングを強化。ドキュメントを更新し、設定の変更点を明示化。
This commit is contained in:
@@ -1,22 +1,28 @@
|
||||
## mainloop モジュール(src-python/mainloop.py)
|
||||
|
||||
このドキュメントは `mainloop.py` の実装と、2025-10-09 に行ったリファクタの概要をまとめます。`mainloop` は標準入力から JSON を受け取り、`controller` のメソッドにルーティングして標準出力へ JSON で応答を返す小さなメインループです。
|
||||
このドキュメントは `mainloop.py` の実装と、最近行ったリファクタの概要をまとめます。`mainloop` は標準入力から JSON を受け取り、`controller` のメソッドにルーティングして標準出力へ JSON で応答を返す小さなメインループです。
|
||||
|
||||
重要な変更点(2025-10-09):
|
||||
- `Main` クラスに `start()` / `stop()` を追加し、受信スレッドとハンドラスレッドのライフサイクル管理を明示化しました。
|
||||
- `queue.get(timeout=...)` を使ってポーリング負荷を下げ、`_stop_event` による安全なシャットダウンを可能にしました。
|
||||
- 標準入力の JSON パースエラーと一般例外のハンドリングを強化しました。
|
||||
- `startReceiver()` / `startHandler()` を使って個別にスレッドを起動することも可能です。
|
||||
重要な変更点:
|
||||
- 2025-10-09: `Main` クラスに `start()` / `stop()` を追加し、受信スレッドとハンドラスレッドのライフサイクル管理を明示化しました。`queue.get(timeout=...)` による安全なシャットダウンを可能にしています。
|
||||
- 2025-10-13: ハンドラの振る舞いを改善しました(マルチワーカー化とロック正規化):
|
||||
- マルチワーカー化: ハンドラ処理はデフォルトで複数ワーカー(例: 3 本)で並列実行されます。これにより、1 つの重い処理が他のすべてのリクエストをブロックしてしまう問題を緩和します。
|
||||
- ロック正規化: `/set/enable/<feature>` と `/set/disable/<feature>` のような on/off ペアは同一のロックキーに正規化され、同一機能の on と off が同時に別スレッドで実行されることを防ぎます。これにより、遅い方の処理結果が後から上書きして最終状態が意図しないものになる不具合を防止します。
|
||||
|
||||
クラス: Main
|
||||
- __init__(controller_instance: Controller, mapping_data: dict) -> None
|
||||
- __init__(controller_instance: Controller, mapping_data: dict, worker_count: int = 3) -> None
|
||||
- `controller_instance`: `Controller` のインスタンス。
|
||||
- `mapping_data`: `mainloop` 内で使用する `mapping`(エンドポイント -> ハンドラ情報)辞書。
|
||||
- `worker_count`: ハンドラワーカー数(デフォルト 3)。実行環境に応じて調整可能です。
|
||||
- start() -> None
|
||||
- 内部で `startReceiver()` と `startHandler()` を呼び、両スレッドを起動します。
|
||||
- 内部で `startReceiver()` と `startHandler()` を呼び、受信とハンドラのスレッド群を起動します。
|
||||
- stop(wait: float = 2.0) -> None
|
||||
- シャットダウンシグナルをセットし、スレッド終了を待ちます(デフォルト 2 秒)。
|
||||
|
||||
動作の重要ポイント
|
||||
- キュー運用: 受信した JSON は内部キューに入れられ、ハンドラワーカーが順次取り出して処理します。`queue.get(timeout=...)` を使っているため CPU 負荷を抑えつつ安全に停止できます。
|
||||
- 同期応答設計: 各エンドポイントは基本的に呼び出し元に同期的に結果を返します(`handler` が戻り値としてステータスと結果を返す)。今回の変更でもこの設計は維持されています。
|
||||
- 同一機能直列化: `/set/enable/X` と `/set/disable/X` のような on/off ペアは内部で同一の "ロックキー" に正規化され、同時に両方が実行されることを防ぎます。これにより、enable と disable が競合して遅い方が勝つ問題が解消されます。
|
||||
|
||||
使い方(例):
|
||||
|
||||
```python
|
||||
@@ -29,15 +35,16 @@ main_instance.start()
|
||||
main_instance.stop()
|
||||
```
|
||||
|
||||
既存のスクリプト互換性:
|
||||
- 既存コードが `startReceiver()` や `startHandler()` を直接呼んでいる場合、そのまま動作します。`start()` / `stop()` を使うと簡潔に起動 / 停止が行えます。
|
||||
確認手順(変更の検証):
|
||||
1. バックエンドを起動しておく。
|
||||
2. UI/テストスクリプトから `/set/enable/translation` と `/set/disable/translation` を高速に交互送信する(数十〜数百ミリ秒間隔で連打)。
|
||||
3. ログ(`printLog` 出力)を確認し、同一機能の複数実行が同時に走っていないこと、最終状態が遅い方に常に上書きされないことを確認する。
|
||||
4. 必要に応じて `worker_count` を増減して挙動を確認する(PC リソースに応じて 1〜6 程度を推奨)。
|
||||
|
||||
注意点と推奨事項:
|
||||
- `stop()` を呼ばないとバックグラウンドスレッドがデーモンであってもプロセス終了前にクリーンアップが不十分になる場合があります。アプリ終了時は `stop()` を呼ぶことを推奨します。
|
||||
- `queue.get(timeout=...)` を使うことで即時性よりも CPU 使用量の低減を優先しています。非常に低レイテンシが必要なケースでは timeout を短くしてください(ただし CPU 使用量に注意)。
|
||||
|
||||
スクリプト連携:
|
||||
- `mainloop.mapping` と `mainloop.run_mapping` は `scripts/print_mapping.py` などのツールから直接参照されます。mapping のキー/値を変更する場合はそれらのスクリプトも確認してください。
|
||||
- `worker_count` を増やすと他のエンドポイントの並列処理性は上がりますが、controller/model 側で共有リソース(GPU メモリやデバイスハンドルなど)への同時アクセスが許可されていない場合は、controller 側で機能単位のロック(例: translation_lock)を追加してください。
|
||||
- このドキュメントの変更は `mainloop` の外側から見える挙動(同期応答、ログ、ロックの方針)を説明するものです。controller 内の処理自体は引き続き同期的に実行されます。必要があれば、enable 系の重い処理を非同期化して完了通知をイベントで返す設計(UI 変更が必要)も検討してください。
|
||||
|
||||
変更履歴:
|
||||
- 2025-10-09: start/stop ライフサイクル、タイムアウト付きキュー取得、エラー処理強化を追加。
|
||||
- 2025-10-13: マルチワーカー化(デフォルト 3)と enable/disable のロック正規化を実装。これにより同一機能の on/off の同時実行を防止し、UI からの高速トグルで最終状態が遅い方に上書きされる問題を修正しました。
|
||||
|
||||
Reference in New Issue
Block a user