Concurrency Patterns: Active Object
Introduction
The Active Object Pattern is a concurrency design pattern that decouples method execution from method invocation. The primary goal of this pattern is to introduce asynchronous behavior by executing operations in a separate thread, while providing a synchronous interface to the client. This is achieved using a combination of message passing, request queues, and scheduling mechanisms.
Key Components
- Proxy: Represents the public interface to the client. In even simpler terms, this is what the client is going to interact to. It translates method calls into requests for the active object.
- Scheduler: Manages the request queue and determines the order of request execution.
- Servant: Contains the actual implementation of the methods being invoked. This is where actual computation logic goes.
- Activation Queue: Stores the requests from the proxy until the scheduler processes them.
- Future/Callback: A placeholder for the result of an asynchronous computation.
Workflow
- A client invokes a method on the proxy.
- The proxy creates a request and places it in the activation queue.
- The scheduler picks up the request and forwards it to the servant for execution.
- The result is returned to the client via a future object.
Use Cases
- Real-time systems requiring predictable execution patterns.
- GUI applications to keep the main thread responsive.
- Distributed systems for handling asynchronous requests.
Implementation
Let's say we need to do a computation, maybe a API call, a database query, etc. I am not going to implement any exception handling because I am too lazy.
def compute(x, y): time.sleep(2) # Some time taking task return x + y
Without Active Object Pattern
Below is an example of how we might handle concurrent requests without using the Active Object Pattern.
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()
Drawbacks of the Above Approach
Thread Management: Direct management of threads increases complexity, especially as the number of tasks grows.
Lack of Abstraction: The client is responsible for managing the lifecycle of threads, coupling task management with business logic.
Scalability Issues: Without a proper queue or scheduling mechanism, there’s no control over task execution order.
Limited Responsiveness: The client has to wait for threads to join before accessing results.
Implementation using Active Object Pattern
Below is a Python implementation of the Active Object Pattern using threading and queues for doing the same thing as above. We'll walk through each part one by one:
MethodRequest: Encapsulates the method, arguments, and a Future to store the result.
def compute(x, y): time.sleep(2) # Some time taking task return x + y
Scheduler: Continuously processes requests from the activation_queue in a separate thread.
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: Implements the actual logic (e.g., the compute method).
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)
Proxy: Translates method calls into requests and returns a Future for the result.
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()
Client: Submits tasks asynchronously and retrieves results when needed.
import time class Servant: def compute(self, x, y): time.sleep(2) return x + y
Advantages
- Decoupled Interface: Clients can invoke methods without worrying about the execution details.
- Responsiveness: Asynchronous execution ensures that the client remains responsive.
- Scalability: Supports multiple concurrent requests.
Disadvantages
- Complexity: Increases architectural complexity.
- Overhead: Requires additional resources for managing threads and queues.
- Latency: Asynchronous processing may introduce additional latency.
Conclusion
The Active Object Pattern is a powerful tool for managing asynchronous operations in multi-threaded environments. By separating method invocation from execution, it ensures better responsiveness, scalability, and a cleaner codebase. While it comes with some complexity and potential performance overhead, its benefits make it an excellent choice for scenarios requiring high concurrency and predictable execution. However, its use depends on the specific problem at hand. As with most patterns and algorithms, there is no one-size-fits-all solution.
References
Wikipedia - Active Object
The above is the detailed content of Concurrency Patterns: Active Object. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Solution to permission issues when viewing Python version in Linux terminal When you try to view Python version in Linux terminal, enter python...

How to avoid being detected when using FiddlerEverywhere for man-in-the-middle readings When you use FiddlerEverywhere...

When using Python's pandas library, how to copy whole columns between two DataFrames with different structures is a common problem. Suppose we have two Dats...

How to teach computer novice programming basics within 10 hours? If you only have 10 hours to teach computer novice some programming knowledge, what would you choose to teach...

How does Uvicorn continuously listen for HTTP requests? Uvicorn is a lightweight web server based on ASGI. One of its core functions is to listen for HTTP requests and proceed...

Using python in Linux terminal...

Fastapi ...

Understanding the anti-crawling strategy of Investing.com Many people often try to crawl news data from Investing.com (https://cn.investing.com/news/latest-news)...
