[Python] Python multiprocessing.Pool hangs under debugpy launcher on Windows ('spawn'), even...

Discussão em 'Python' iniciado por Stack, Maio 3, 2025.

  1. Stack

    Stack Membro Participativo

    I'm encountering an issue where my Python script using multiprocessing.Pool hangs during startup, but only when launched via the debugpy debugger/launcher integrated with VS Code on Windows. When run directly from the terminal (python my_script.py), the script works correctly and utilizes the multiprocessing pool as expected.

    Environment:

    OS: Windows

    Python: 3.12

    Multiprocessing Start Method: 'spawn' (default on Windows)

    Debugging Context: Launched via debugpy (standard VS Code Python debugging configuration Python: Current File)

    Problem:

    The script successfully completes its initial setup phases (logging, parameter loading, task list generation). However, when it reaches the point where it initializes or starts using the multiprocessing.Pool, it hangs indefinitely. No error messages or tracebacks are produced in the main process; it simply stops progressing right before the pool begins processing tasks.

    Minimal Example Structure:

    My actual script is quite complex (scientific simulation), but the core structure encountering the hang can be represented by this minimal example:

    import multiprocessing
    from multiprocessing import freeze_support #, set_start_method # Tried adding this too
    import time
    import os
    import sys
    import logging # Using basic logging setup similar to full script

    # --- Worker function (Defined globally) ---
    # Simplified worker, the real one calls a complex simulation function
    def simple_worker_function(task_arg):
    pid = os.getpid()
    print(f"--- [Worker {pid}] Started task: {task_arg} ---", file=sys.stderr, flush=True)
    time.sleep(0.1) # Simulate work
    result = task_arg * task_arg
    print(f"--- [Worker {pid}] Finished task: {task_arg} ---", file=sys.stderr, flush=True)
    # In reality, returns a tuple: (task_id_tuple, result_dictionary)
    return (task_arg, result) # Simple return for example

    # --- Main Execution Guard ---
    if __name__ == "__main__":
    # --- Essential setup for multiprocessing on Windows/spawn ---
    freeze_support()
    # --- Tried adding this, didn't help (might give warning if context already set) ---
    # try:
    # set_start_method('spawn')
    # except RuntimeError:
    # pass

    # --- Simulate Script Setup ---
    logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)-8s] %(message)s')
    logging.info("Main: Initial setup complete.")
    # Simulate generating task arguments
    task_arguments = list(range(20)) # Example dummy tasks
    logging.info(f"Main: Generated {len(task_arguments)} tasks.")

    # --- Execution Phase ---
    execution_start_time = time.time()
    results_from_workers = {} # Store results

    if not task_arguments:
    logging.info("Main: No tasks to run.")
    else:
    try:
    # Determine number of workers
    logical_cores = os.cpu_count() or 2
    num_workers = min(max(1, logical_cores - 1), len(task_arguments))
    logging.info(f"Main: Preparing to start Pool with {num_workers} workers.")

    # <<< ----- HANG OCCURS AROUND HERE WHEN RUN WITH DEBUGPY ----- >>>

    with multiprocessing.Pool(processes=num_workers) as pool:
    logging.info("Main: Pool created. Submitting tasks using map...")
    # Tried pool.imap_unordered(...) as well, same hang behavior under debugpy

    # Use map (blocks until all done) for simpler debugging attempt
    worker_outputs = pool.map(simple_worker_function, task_arguments)

    logging.info(f"Main: Pool.map finished, processing {len(worker_outputs)} results...")
    for task_id_out, result_val in worker_outputs:
    results_from_workers[str(task_id_out)] = result_val # Simulate storing results

    logging.info(f"Main: Finished processing pool results. {len(results_from_workers)} tasks completed.")

    except KeyboardInterrupt:
    logging.warning("Main: Keyboard Interrupt during execution.")
    except Exception as e:
    logging.error(f"Main: Error during multiprocessing execution: {e}", exc_info=True)

    # --- Final Summary ---
    execution_duration = time.time() - execution_start_time
    logging.info(f"--- Main: Execution phase completed in {execution_duration:.2f} sec ---")
    logging.info(f"--- Main: Script Finished ---")



    What Happens:

    When run directly (python my_script.py), it prints "Main: Preparing...", "Main: Generated...", "Main: Preparing to start Pool...", "Main: Pool created...", "Main: Submitting...", worker messages (to stderr), "Main: Pool.map finished...", "Main: Finished processing...", and "Main: Script Finished.".

    When run via the debugpy launcher (Python: Current File in VS Code), it prints "Main: Initial setup complete.", "Main: Generated ... tasks.", "Main: Preparing to start Pool with ... workers." and then hangs indefinitely. It never prints "Main: Pool created..." or any subsequent messages.

    What I've Tried:

    freeze_support(): Added this as the first line inside if name == "main":.

    Global Worker Function: The worker function (simple_worker_function here, run_single_task_wrapper in my real code) is defined at the top level of the module, not inside the main block. This fixed an earlier AttributeError.

    Explicit set_start_method('spawn'): Added this after freeze_support(). It sometimes generates a harmless warning that the context is already set but doesn't fix the hang.

    pool.map vs pool.imap_unordered: The hang occurs regardless of which pool method is used.

    Simplified Worker: Even using a dummy worker function that does almost nothing (like in the example above) still results in the hang under debugpy.

    Sequential Execution: If I bypass the Pool entirely and run the worker function sequentially in a simple for loop within the main process, it works fine even under debugpy.

    Question:

    Why does the script hang specifically during the multiprocessing.Pool creation or usage phase when launched via debugpy on Windows, given that the standard freeze_support() and global worker function fixes have been applied? Is there a known conflict or a specific way debugpy interferes with the spawn process creation that requires additional workarounds beyond the standard ones? How can I diagnose this further or resolve the hang to allow debugging of the parallel part?

    Continue reading...

Compartilhe esta Página