ホームページ > バックエンド開発 > Python チュートリアル > 同時実行パターン: アクティブ オブジェクト

同時実行パターン: アクティブ オブジェクト

Mary-Kate Olsen
リリース: 2024-12-24 19:27:20
オリジナル
751 人が閲覧しました

Concurrency Patterns: Active Object

導入

アクティブ オブジェクト パターンは、メソッドの実行メソッドの呼び出しから切り離す同時実行設計パターンです。このパターンの主な目的は、クライアントに同期インターフェイスを提供しながら、別のスレッドで操作を実行することによって非同期動作を導入することです。これは、メッセージ パッシング、リクエスト キュー、スケジューリング メカニズムを組み合わせて使用​​して実現されます。

主要コンポーネント

  1. プロキシ: クライアントへのパブリック インターフェイスを表します。さらに簡単に言えば、これはクライアントが対話する対象です。メソッド呼び出しをアクティブなオブジェクトのリクエストに変換します。
  2. スケジューラ: リクエスト キューを管理し、リクエストの実行順序を決定します。
  3. Servant: 呼び出されるメソッドの実際の実装が含まれます。ここが実際の計算ロジックの始まりです。
  4. アクティブ化キュー: スケジューラが処理するまで、プロキシからのリクエストを保存します。
  5. Future/Callback: 非同期計算の結果のプレースホルダー。

ワークフロー

  1. クライアントはプロキシ上でメソッドを呼び出します。
  2. プロキシはリクエストを作成し、アクティベーションキューに入れます。
  3. スケジューラはリクエストを取得し、実行のためにサーバントに転送します。
  4. 結果は、future オブジェクトを介してクライアントに返されます。

使用例

  • 予測可能な実行パターンを必要とするリアルタイム システム。
  • メインスレッドの応答性を維持するための GUI アプリケーション。
  • 非同期リクエストを処理するための分散システム。

実装

API 呼び出しやデータベース クエリなどの計算を行う必要があるとします。私は怠け者なので、例外処理は実装しません。

def compute(x, y):
    time.sleep(2)  # Some time taking task
    return x + y
ログイン後にコピー
ログイン後にコピー

アクティブオブジェクトパターンなし

以下は、アクティブ オブジェクト パターンを使用せずに同時リクエストを処理する方法の例です。

import threading
import time


def main():
    # Start threads directly
    results = {}

    def worker(task_id, x, y):
        results[task_id] = compute(x, y)

    print("Submitting tasks...")
    thread1 = threading.Thread(target=worker, args=(1, 5, 10))
    thread2 = threading.Thread(target=worker, args=(2, 15, 20))

    thread1.start()
    thread2.start()

    print("Doing other work...")

    thread1.join()
    thread2.join()

    # Retrieve results
    print("Result 1:", results[1])
    print("Result 2:", results[2])


if __name__ == "__main__":
    main()
ログイン後にコピー
ログイン後にコピー

上記のアプローチの欠点

  • スレッド管理: スレッドを直接管理すると、特にタスクの数が増えるにつれて複雑さが増します。

  • 抽象化の欠如: クライアントは、タスク管理とビジネス ロジックを結び付けて、スレッドのライフサイクルを管理する責任があります。

  • スケーラビリティの問題: 適切なキューまたはスケジューリング メカニズムがなければ、タスクの実行順序を制御できません。

  • 応答性の制限: クライアントは、結果にアクセスする前にスレッドが参加するまで待つ必要があります。

Active Objectパターンを使用した実装

以下は、上記と同じことを行うためにスレッドとキューを使用したアクティブ オブジェクト パターンの Python 実装です。各部分を 1 つずつ説明します:

MethodRequest: メソッド、引数、および結果を保存する Future をカプセル化します。

def compute(x, y):
    time.sleep(2)  # Some time taking task
    return x + y
ログイン後にコピー
ログイン後にコピー

スケジューラ: 別のスレッドで activity_queue からのリクエストを継続的に処理します。

import threading
import time


def main():
    # Start threads directly
    results = {}

    def worker(task_id, x, y):
        results[task_id] = compute(x, y)

    print("Submitting tasks...")
    thread1 = threading.Thread(target=worker, args=(1, 5, 10))
    thread2 = threading.Thread(target=worker, args=(2, 15, 20))

    thread1.start()
    thread2.start()

    print("Doing other work...")

    thread1.join()
    thread2.join()

    # Retrieve results
    print("Result 1:", results[1])
    print("Result 2:", results[2])


if __name__ == "__main__":
    main()
ログイン後にコピー
ログイン後にコピー

Servant: 実際のロジック (計算メソッドなど) を実装します。

class MethodRequest:
    def __init__(self, method, args, kwargs, future):
        self.method = method
        self.args = args
        self.kwargs = kwargs
        self.future = future

    def execute(self):
        try:
            result = self.method(*self.args, **self.kwargs)
            self.future.set_result(result)
        except Exception as e:
            self.future.set_exception(e)
ログイン後にコピー

プロキシ: メソッド呼び出しをリクエストに変換し、結果の Future を返します。

import threading
import queue


class Scheduler(threading.Thread):
    def __init__(self):
        super().__init__()
        self.activation_queue = queue.Queue()
        self._stop_event = threading.Event()

    def enqueue(self, request):
        self.activation_queue.put(request)

    def run(self):
        while not self._stop_event.is_set():
            try:
                request = self.activation_queue.get(timeout=0.1)
                request.execute()
            except queue.Empty:
                continue

    def stop(self):
        self._stop_event.set()
        self.join()
ログイン後にコピー

クライアント: タスクを非同期で送信し、必要に応じて結果を取得します。

import time


class Servant:
    def compute(self, x, y):
        time.sleep(2)
        return x + y
ログイン後にコピー

利点

  • 分離されたインターフェイス: クライアントは実行の詳細を気にせずにメソッドを呼び出すことができます。
  • 応答性: 非同期実行により、クライアントの応答性が確保されます。
  • スケーラビリティ: 複数の同時リクエストをサポートします。

短所

  • 複雑さ: アーキテクチャの複雑さが増加します。
  • オーバーヘッド: スレッドとキューを管理するために追加のリソースが必要です。
  • レイテンシ: 非同期処理により追加のレイテンシが発生する可能性があります。

結論

アクティブ オブジェクト パターンは、マルチスレッド環境で非同期操作を管理するための強力なツールです。メソッドの呼び出しを実行から分離することで、応答性、スケーラビリティが向上し、コードベースがよりクリーンになります。ある程度の複雑さと潜在的なパフォーマンスのオーバーヘッドは伴いますが、その利点により、高い同時実行性と予測可能な実行が必要なシナリオには優れた選択肢となります。ただし、その使用は当面の特定の問題によって異なります。ほとんどのパターンやアルゴリズムと同様、万能の解決策はありません。

参考文献

ウィキペディア - アクティブ オブジェクト

以上が同時実行パターン: アクティブ オブジェクトの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート