Asynchronous Task Queue in Python
Building a task queue is a fundamental pattern in distributed systems and asynchronous programming. This challenge asks you to implement a simplified task queue in Python, allowing you to defer the execution of functions to be processed later, improving application responsiveness and scalability. This is useful for handling long-running operations like sending emails, processing images, or making API calls without blocking the main thread.
Problem Description
You are to implement a TaskQueue class in Python. This class should manage a queue of tasks, where each task is a function to be executed along with its arguments. The queue should support adding tasks, processing tasks, and checking the queue's status.
Key Requirements:
add_task(func, *args, **kwargs): Adds a task to the queue. The task is represented by a functionfuncand its positional and keyword arguments*argsand**kwargs.process_task(): Removes and executes the next task from the queue. If the queue is empty, it should returnNone.is_empty(): ReturnsTrueif the queue is empty,Falseotherwise.size(): Returns the number of tasks currently in the queue.
Expected Behavior:
The add_task method should enqueue the task (function and arguments) for later execution. The process_task method should dequeue the next task, call the function with the provided arguments, and return the result of the function call. The is_empty and size methods should accurately reflect the queue's state.
Edge Cases to Consider:
- Empty queue when
process_task()is called. - Tasks that raise exceptions during execution. The
process_taskmethod should not crash the entire queue; it should handle exceptions gracefully (e.g., by logging the error and returningNone). - Functions with no arguments.
- Functions with only keyword arguments.
- Functions with both positional and keyword arguments.
Examples
Example 1:
Input:
queue = TaskQueue()
queue.add_task(lambda x: x * 2, 5)
result = queue.process_task()
Output:
10
Explanation: The lambda function `lambda x: x * 2` is added to the queue with the argument 5. `process_task()` dequeues the function, calls it with 5, and returns the result (10).
Example 2:
Input:
queue = TaskQueue()
queue.add_task(lambda x, y=10: x + y, 3, y=5)
result = queue.process_task()
Output:
8
Explanation: The lambda function `lambda x, y=10: x + y` is added to the queue with the argument 3 and keyword argument y=5. `process_task()` dequeues the function, calls it with 3 and y=5, and returns the result (8).
Example 3: (Edge Case - Empty Queue)
Input:
queue = TaskQueue()
result = queue.process_task()
Output:
None
Explanation: The queue is empty. `process_task()` returns None.
Constraints
- The queue should be implemented using a standard Python data structure (e.g.,
list,collections.deque). - The functions added to the queue can be any callable object (functions, lambdas, methods).
- The
process_taskmethod should handle exceptions raised by the executed functions without crashing the queue. Logging the exception is recommended but not strictly required. - The time complexity of
add_taskshould be O(1). - The time complexity of
process_taskshould be O(1). - The time complexity of
is_emptyshould be O(1). - The time complexity of
sizeshould be O(1).
Notes
Consider using a deque from the collections module for efficient queue operations. Think about how to handle exceptions gracefully within the process_task method to prevent the entire queue from failing if a task encounters an error. The focus is on the core queue functionality; error handling and advanced features like prioritization or concurrency are not required for this challenge.