Adding to @Blckknght's answer: on Windows, each process imports the original module "from scratch", while on Unix-y systems only the main process runs the whole module, while all other processes see whatever exists at the time fork()
is used to create the new processes (no, you're not calling fork()
yourself - multiprocessing
internals call it whenever it creates a new process).
In detail, for your import_mock
:
On all platforms, the main process calls func()
, which sets import_mock.to_mock
to 1.
On Unix-y platforms, that's what all new processes see: the fork()
occurs after that, so 1 is the state all new processes inherit.
On Windows, all new processes run the entire module "from scratch". So they each import their own, brand new version of import_mock
. Only the main process calls func()
, so only the main process sees to_mock
change to 1. All other processes see the fresh None
state.
That's all expected, and actually easy to understand the second time ;-)
What's going on with passing a
is subtler, because it depends more on multiprocessing
implementation details. The implementation could have chosen to pickle arguments on all platforms from the start, but it didn't, and now it's too late to change without breaking stuff on some platforms.
Because of copy-on-write fork()
semantics, it wasn't necessary to pickle Process()
arguments on Unix-y systems, and so the implementation never did. However, without fork()
it is necessary to pickle them on Windows - and so the implementation does.
Before Python 3.4, which allows you to force "the Windows implementation" (spawn
) on all platforms, there's no mechanical way to avoid possible cross-platform surprises.
But in practice, I've rarely been bothered by this. Knowing that, for example, multiprocessing can depend heavily on pickling, I stay completely clear of getting anywhere near playing tricks with pickles. The only reason you had "a problem" passing an A()
instance is that you are playing pickle tricks (via overriding the default __getstate__()
).