A process runs in its own memory space and it doesn’t share memory with other processes, but threads run inside of a process in the same memory space.
Race Condition: The OS will allocate memory to the process to be used at the runtime and to be accessed by all threads. It can happen that a thread would like to read data from a specific memory space, but the other thread is not yet loaded data into that memory space, which will lead to an error. We cannot tell the order in which the threads will try to reach the shared data.
Solution: Global Interpreter Lock (GIL). GIL do not allows for multiple threads to execute python code at the same time.
Multithreading
- It uses only one CPU for all of the threads.
- The code will not run faster if it’s already using 100% CPU.
- Python threads are best used in cases where the execution of a task involves some waiting (ie.: I/O operations)
- The threading module is used to start multiple threads inside a single process.
Pros
- Lightweight, lower memory consumption
- Shared memory, makes access to state from another context easier
- Allows you to easily make responsive UIs
- Great option for I/O applications
Cons
- CPython, subject to the GIL
- Threads are not killable
- If not following a command queue / message model, then manual use of synchronization becomes necessary
- Code is usually harder to understand and to get right due to the potential of race conditions increases dramatically
Multiprocessing
- Low risk of data-corruption when using multiprocessing
- Each process has its own allocated memory
- Each process has it’s own GIL so there is no resource conflict or race condition
Pros
- Separate memory space
- Code is usually straight forward
- Takes advantage on multiple CPU cores
- Avoids GIL limitations
- Child processes are interruptable
Cons
- Larger memory consumption
- Inter Process Communication (IPC) a little more complicated with more overhead
https://docs.python.org/2.7/library/queue.html