Why are multiple debugging threads being started in integrated Python-debugging of VS Code?

Viewed 1611

** UPDATE **

From a related discussion I got the following answer:

Also, something to double check in your code is if the main entry point is protected by the __name__=='__main__' check:

if __name__ == '__main__':
    main()

and depending on the structure, you could also need the multiprocessing.freeze_support() if you need to have multiprocessing support:

i.e.:

if __name__ == '__main__':
    multiprocessing.freeze_support()
    main()

(if you don't have that structure, it may explain why multiprocessing is executing your main entry point code multiple times).

Yet, I have neither the first nor the second aforementioned option implemented and moreover, this sudden multi-threading behavior could happen with any script I execute. It seems like old/previous debugging sessions are not really terminated behind the scenes, because in one session there is no multi-threading and then, later, a multi-session is launched when debugging. This has also happened when executing the code normally and it exited with errors, and THEN I went (again) into a debugging session.

I'm not sure how to implement it, but should I put

if __name__ == '__main__':
    multiprocessing.freeze_support()
    main()

in the beginning of all my python-scripts which I execute as main-script, i.e. my entry-point or top-level script?

** ORIGINAL QUESTION **

As it can be seen in the attached screenshot, while carrying out the standard debugging process via pressing F5 in VS Code (with some breakpoints defined previously), occasionally the following happens:

Python debugging in VS Code

This causes a lot of additional computing time, lagging and redundant console-output as the same code is executed several times in a row.

Oddly enough, this behavior seems to happen randomly. Only sometimes I could conclude that I attempted to launch a debugging session previously, which was interrupted at some point within the code by an Uncaught Exception, but still it doesn't seem logical to me why VS Code should relaunch all these previously failed debugging sessions along with a new one when pressing F5.

When interrupting the debugging procedure via Shift+F5 or the red square button Stop, sometimes the message timeout after 1000 ms appears on the bottom right of the VS Code - window; especially when the issue occurred showing multiple undesired subprocesses and threads in the call stack.

The relevant part of my launch.json - file defining the integrated terminal debugging is:

{
    // !!THIS CONFIG-FILE IS USED FOR DEBUGGING!!
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File (Integrated Terminal)",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            // VS Code Integrated Terminal. If redirectOutput is set to True, output is also displayed in the debug console.
            "console": "integratedTerminal",
            // Make sure that code output is also being displayed in the debug console.
            "redirectOutput": true,
            // When omitted or set to true (the default), restricts debugging to user-written code only. Set to false to also enable debugging of standard library functions.
            "justMyCode": false
        },
...

As for the settings.json - file, I haven't implemented any options concerning python debugging whatsoever, apart from the generic option "debug.allowBreakpointsEverywhere": true.

I'd like to understand why this undesired debugging behavior crops up in the first place and finally to be able to prevent it from occurring.


PS on trying to get an answer on the official GitHub of VS Code:

I'd filed an issue on the VS Code GitHub-page, but was adviced to ask on StackOverflow since

"VS Code only shows what is being requested by the Python debugger."

2 Answers

Is your code multi-threaded or are these processes from you debugging in the past (but those processes not being killed off as intended)?

Personally I'm not observing this issue when going through Run -> Start Debugging- which I believe is the same as F5.

This is what my call stack section looks like as well my launch.json. Would make sure to hit stop or the disconnect button after you're done with a debugging session.

enter image description here

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        }
    ]
}

I found the solution in this thread.

To sum it up,

it is recommended to implement in the end of the main script (i.e. the starting entry-script from where the execution begins) the following:

if __name__ == '__main__':
    multiprocessing.freeze_support()
    main()

The line multiprocessing.freeze_support() won't do harm/nothing on UNIX-systems and works on Windows. If the reader likes to learn more in-depth knowledge about this function, see the docs and this part of the GitHub-discussion mentioned above.

The essential part though (see here)

is calling main() from "under the guard"

where "guard" stands for if __name__ == '__main__':.

Furthermore, as it had been explained here,

it should generally be at the end of your script, and all top-level code that's not already in a function should be inside main(). The reason is that your script is going to be imported as a module in every subprocess that multiprocessing spawns, so any top-level code will run many times without the guard. But only the instance that's directly started as a script will have __name__ == "__main__".

Related