[Python] Checking Process Status with multiprocessing.Process.is_alive()

目次

Overview

To determine if a process created with multiprocessing is currently running or has already finished, you can use the is_alive() method. By also checking the process ID (pid) and the exitcode, you can manage the status of your processes in detail. This article explains how to monitor multiple asynchronous processes using a monitoring loop in the main process.

Specifications (Input/Output)

  • Input: A generated multiprocessing.Process object.
  • Output:
    • is_alive(): Returns True if the process is running, otherwise False (not started or finished).
    • pid: The process ID (valid only while running).
    • exitcode: The exit code (valid only after finishing. 0 indicates normal termination).
  • Behavior: The main process runs a loop to poll (query) the status of each sub-process at regular intervals and displays the results.

Basic Usage

Call the method on the process object.

if process.is_alive():
    print(f"Running (PID: {process.pid})")
else:
    print(f"Finished (ExitCode: {process.exitcode})")

Full Code Example

The following code starts two processes with different execution times and continues to report their status from the main process until both have finished.

import multiprocessing
import time
import os
import random

def worker_task(name: str, duration: int):
    """
    A task that runs for a specified duration.
    """
    print(f"  [{name}] Started (PID: {os.getpid()}) Duration: {duration}s")
    # Simulate processing
    for i in range(duration):
        time.sleep(1)
    print(f"  [{name}] Finished")

def main():
    print("--- Main Process Started ---")

    # Create two processes with different durations
    # process1: Finishes in 3 seconds
    p1 = multiprocessing.Process(
        target=worker_task, 
        kwargs={"name": "Process-1", "duration": 3}
    )
    # process2: Finishes in 6 seconds
    p2 = multiprocessing.Process(
        target=worker_task, 
        kwargs={"name": "Process-2", "duration": 6}
    )

    # Start the processes
    p1.start()
    p2.start()

    # Monitoring loop
    # Continue the loop as long as at least one process is alive
    while p1.is_alive() or p2.is_alive():
        print(f">>> Monitoring: P1={p1.is_alive()}, P2={p2.is_alive()}")
        
        if not p1.is_alive() and p1.exitcode is not None:
             print(f"    (P1 has finished. ExitCode: {p1.exitcode})")
        
        time.sleep(1.0)

    print("--- All processes finished detected ---")

    # Resource cleanup (join should be called even if is_alive is False to prevent zombies)
    p1.join()
    p2.join()
    
    print("All tasks are complete.")

if __name__ == "__main__":
    main()

Example Output

You can see that Process-1 finishes first, followed by Process-2.

--- Main Process Started ---
  [Process-1] Started (PID: 12345) Duration: 3s
  [Process-2] Started (PID: 12346) Duration: 6s
>>> Monitoring: P1=True, P2=True
>>> Monitoring: P1=True, P2=True
>>> Monitoring: P1=True, P2=True
  [Process-1] Finished
>>> Monitoring: P1=False, P2=True
    (P1 has finished. ExitCode: 0)
>>> Monitoring: P1=False, P2=True
    (P1 has finished. ExitCode: 0)
  [Process-2] Finished
--- All processes finished detected ---
All tasks are complete.

Customization Points

  • Adjusting the Polling Interval: Shortening the time.sleep(1.0) value makes detection faster but increases CPU load. Adjust this according to your needs.
  • Changing Monitoring Conditions:
    • while p1.is_alive() or p2.is_alive(): waits while at least one process is running.
    • Changing it to and will loop only while both are running, exiting the loop as soon as one finishes.
  • Forced Termination: If a process takes too long, you can add logic to call process.terminate() to force it to stop.

Important Notes

  • Importance of join(): Even if is_alive() becomes False, process information remains in memory until join() is called (this is known as a zombie process). Always execute join() after the monitoring loop.
  • Status Before start(): is_alive() returns False immediately after the process instance is created (before start()). Be careful to distinguish between “not yet started” and “already finished.”
  • Exitcode Timing: While the process is running, exitcode is None. It only receives an integer value once the process has finished.

Advanced Application

This pattern uses join(timeout) for non-blocking waiting. Instead of manually sleeping in a while loop, you use the timeout feature of join to repeat the action: “wait until finished or until the specified time has passed.”

import multiprocessing
import time

def task():
    time.sleep(2)

if __name__ == "__main__":
    p = multiprocessing.Process(target=task)
    p.start()
    
    # Repeat until the process ends
    while p.is_alive():
        print("Still running...")
        # Wait for 0.5 seconds. Returns immediately if finished.
        p.join(timeout=0.5)
    
    print("Finished")

Conclusion

Using is_alive() allows you to visualize the progress of parallel processing, which can otherwise be a “black box.”

Reminder: Calling join() to release resources is mandatory even after monitoring is finished. By actively checking the status instead of just waiting with p.join(), you can implement advanced and responsive parallel processing workflows.

Best for: Displaying progress bars, health monitoring, or starting the next task as soon as one process ends.

Key Point: Configure the monitoring loop condition (or vs and) based on your specific requirements.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次