杰瑞科技汇

python after cancel

Of course! The phrase "python after cancel" can mean a few different things. I'll cover the most common interpretations:

python after cancel-图1
(图片来源网络,侵删)
  1. Canceling a running thread or process.
  2. Canceling a multiprocessing task.
  3. Canceling a concurrent.futures task.
  4. Canceling an asynchronous task (asyncio).
  5. The meaning of the cancel() method in other contexts (like iterators).

Let's break down each one with code examples.


Canceling a Running Thread

In Python's standard threading module, you cannot forcefully kill a running thread. This is a deliberate design choice for stability. Killing a thread while it's holding a lock or updating shared resources can lead to deadlocks or corrupted data.

Instead, you use a cooperative approach with a shared flag.

How it works:

  • A threading.Event or a simple threading.Event is used as a signal.
  • The main thread sets the event to signal the worker thread to stop.
  • The worker thread periodically checks this event. If it's set, it cleans up and exits gracefully.

Example:

import threading
import time
def worker(stop_event):
    """A worker function that runs until the stop_event is set."""
    print("Worker: Starting...")
    while not stop_event.is_set():
        print("Worker: Running...")
        time.sleep(1)  # Simulate work
    print("Worker: Stop signal received. Cleaning up and exiting.")
# Create a stop event
stop_event = threading.Event()
# Create and start the thread
thread = threading.Thread(target=worker, args=(stop_event,))
thread.start()
# Let the worker run for a few seconds
print("Main: Waiting for 3 seconds...")
time.sleep(3)
# "Cancel" the thread by setting the stop event
print("Main: Sending stop signal to the worker thread.")
stop_event.set()
# Wait for the thread to finish its cleanup
thread.join()
print("Main: Worker thread has terminated.")

Output:

python after cancel-图2
(图片来源网络,侵删)
Main: Waiting for 3 seconds...
Worker: Starting...
Worker: Running...
Worker: Running...
Worker: Running...
Main: Sending stop signal to the worker thread.
Worker: Stop signal received. Cleaning up and exiting.
Main: Worker thread has terminated.

Canceling a multiprocessing.Process

This is more straightforward than threads because processes run in their own memory space. You can terminate them directly, but it should be used with caution.

How it works:

  • The process.terminate() method sends a SIGTERM signal (on Unix-like systems) or forces a termination (on Windows).
  • This does not give the process a chance to clean up. It can lead to resource leaks (e.g., unclosed files, network connections).
  • The safer way is to use a multiprocessing.Event for a graceful shutdown, just like with threads.
  • After terminating, you should always call process.join() to ensure the process has fully cleaned up.

Example (Forced Termination):

import multiprocessing
import time
def worker():
    """A worker function that runs for a long time."""
    print("Process: Starting...")
    try:
        while True:
            print("Process: Running...")
            time.sleep(1)
    except KeyboardInterrupt:
        print("Process: Caught KeyboardInterrupt. Cleaning up...")
if __name__ == "__main__":
    process = multiprocessing.Process(target=worker)
    process.start()
    print("Main: Waiting for 3 seconds...")
    time.sleep(3)
    # "Cancel" the process by terminating it
    print("Main: Terminating the process.")
    process.terminate() # This is the "cancel" action
    process.join() # Wait for the process to be fully terminated
    print("Main: Process has been terminated.")

Note: The print("Process: Caught KeyboardInterrupt...") line likely won't be executed because terminate() is abrupt.


Canceling a concurrent.futures Task

This is the modern and recommended way to manage background tasks. The concurrent.futures module provides a high-level interface for asynchronous execution.

How it works:

  • You submit a function to an Executor (e.g., ThreadPoolExecutor or ProcessPoolExecutor), which returns a Future object.
  • The Future object has a cancel() method.
  • You can only cancel a task that has not yet started running. If the task is already running, cancel() will return False.
  • If the task is in the queue and hasn't been picked up by a thread/process, cancel() will return True.

Example:

import concurrent.futures
import time
def long_running_task(seconds):
    """A task that runs for a given number of seconds."""
    print(f"Task: Starting, will run for {seconds} seconds.")
    time.sleep(seconds)
    print(f"Task: Finished after {seconds} seconds.")
    return f"Result after {seconds} seconds"
if __name__ == "__main__":
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
        # Submit a task that will take 5 seconds to run
        future = executor.submit(long_running_task, 5)
        print(f"Future: Task submitted. Cancelled status: {future.cancelled()}")
        # Wait for 2 seconds, then try to cancel
        time.sleep(2)
        print("Main: Attempting to cancel the future...")
        cancelled = future.cancel()
        print(f"Future: Cancel attempt successful? {cancelled}. Cancelled status: {future.cancelled()}")
        # Check if the future was actually cancelled
        if future.cancelled():
            print("Main: The task was successfully cancelled.")
        else:
            print("Main: The task could not be cancelled (it's already running).")
            # You can still get the result or handle any exception
            try:
                result = future.result(timeout=1) # Wait a bit for it to finish
                print(f"Main: Task result: {result}")
            except concurrent.futures.CancelledError:
                print("Main: Task was cancelled, so getting its result raises CancelledError.")
            except Exception as e:
                print(f"Main: Task raised an exception: {e}")

Canceling an asyncio Task

In asyncio, cancellation is a core feature and is handled gracefully using asyncio.CancelledError.

python after cancel-图3
(图片来源网络,侵删)

How it works:

  • asyncio.create_task() returns a Task object.
  • The task.cancel() method schedules a CancelledError to be raised inside the coroutine at the next await point.
  • The coroutine must catch CancelledError to perform any necessary cleanup before exiting.

Example:

import asyncio
async def long_running_coroutine():
    """A coroutine that runs for several seconds."""
    print("Coroutine: Starting...")
    try:
        # asyncio.sleep is a point where the event loop can check for cancellation
        await asyncio.sleep(5)
        print("Coroutine: Finished successfully.")
    except asyncio.CancelledError:
        print("Coroutine: Cancelled! Cleaning up...")
        # Perform any cleanup here
        await asyncio.sleep(1) # Simulate cleanup work
        print("Coroutine: Cleanup complete.")
        # You must re-raise the exception to properly terminate the task
        raise
async def main():
    # Create the task
    task = asyncio.create_task(long_running_coroutine())
    # Let it run for a bit
    await asyncio.sleep(2)
    print("Main: Cancelling the task...")
    task.cancel()
    # Wait for the task to be cancelled and cleaned up.
    # We use 'try/except' to catch the CancelledError that propagates from the task.
    try:
        await task
    except asyncio.CancelledError:
        print("Main: Task was cancelled as expected.")
if __name__ == "__main__":
    asyncio.run(main())

The cancel() Method in Other Contexts

The cancel() method also appears in other Python objects, with a similar "stop what you're doing" meaning.

Example: asyncio.Task (already covered above)

Example: threading.Timer

A Timer can be canceled before it has a chance to start its target function.

import threading
import time
def hello():
    print("Hello, World!")
# Create a timer that will run 'hello' in 5 seconds
timer = threading.Timer(5.0, hello)
print("Timer: Started. Will run in 5 seconds.")
# Cancel the timer after 2 seconds
time.sleep(2)
timer.cancel()
print("Timer: Cancelled.")
# If the timer was cancelled, hello() will never be printed.
# You can check if it was "active"
print(f"Timer was active? {timer.is_alive()}")

Example: itertools.chain or other iterators

Some iterators have a close() method to signal that they should stop producing items, which is a form of cancellation.

import itertools
def my_generator():
    print("Generator: Starting...")
    try:
        for i in itertools.count():
            print(f"Generator: yielding {i}")
            yield i
    except GeneratorExit:
        print("Generator: Closed! Performing cleanup.")
        # No need to re-raise GeneratorExit
gen = my_generator()
next(gen)  # Get the first item
next(gen)  # Get the second item
print("Main: Closing the generator.")
gen.close() # This is the "cancel" action for a generator
# If you call next(gen) now, it will raise StopIteration

Summary: Which one to use?

Scenario Recommended Tool How to "Cancel"
Running in a thread threading.Event + threading.Thread Set the event from the main thread; the worker thread checks and exits.
Running in a separate process multiprocessing.Event + multiprocessing.Process (for graceful shutdown) Use process.terminate() for forceful, abrupt stop.
Running background tasks (modern) concurrent.futures Submit task, get Future, call future.cancel().
Running asynchronous I/O tasks asyncio Create task, get Task, call task.cancel(). Catch CancelledError in the coroutine.
A function that needs to run later threading.Timer Call timer.cancel() before the timer elapses.
分享:
扫描分享到社交APP
上一篇
下一篇