How to choose between several parallel methods available in Python?

Here are different ways to perform simultaneous actions in Python - this is a simple list:

  • process-based: process.Popen , multiprocessing.Process , old-fashioned os.system , os.popen , os.exe*
  • thread based: threading.Thread
  • microthread: greenlet

I know the difference between thread-based concurrency and process-based concurrency, and I know some (but not too much) about the GIL impact on CPython thread support.

For beginners who want to implement some level of concurrency, how to choose between them? Or what is the common difference between the two? Are there any more ways to do simultaneous in Python?

I am not sure if I am asking the right question, please feel free to improve this question.

+4
source share
1 answer

The reason for all three of these mechanisms is that they have different strengths and weaknesses.

Firstly, if you have a huge number of small independent tasks, and there is no reasonable way to start them (usually this means that you are writing a C10k server, but this is not the only possible case), microthreads win hands down. You can run just a few hundred threads or OS processes before everything either fades or just works. This way you use microflows or refuse automatic concurrency and start writing explicit callbacks or coroutines. This is truly the only moment when microflows win; otherwise, they are similar to OS threads, except that some of them do not work correctly.

Then, if your code is tied to a CPU , you need processes. Microflows are an integral single-core solution; Threads in Python usually cannot parallelize well due to the GIL; processes get as much parallelism as the OS can handle. Thus, processes will allow your 4-core system to quickly run your 4x code; there will be nothing more. (Actually, you might want to go ahead and distribute them on different computers, but they didn’t ask about it.) But if your code is I / O-bound , core-parallelism does not help, so the threads are as good as the processes .

If you have a lot of common, mutable data, everything will be complicated. Processes require you to explicitly put everything in common structures, for example, multiprocessing.Array instead of list , which becomes terribly complicated. Themes share everything automatically - this means that everywhere there are race conditions. This means that you need to carefully consider your flow and use locks effectively. With processes, experienced developers can create a system that works in all test data, but it needs to be reorganized every time you give it a new set of inputs. Using threads, an experienced developer can write code that works a few weeks before randomly and silently scrambling all credit card numbers.

Which of these two scares you the most - do it because you better understand the problem. Or, if at all possible, step back and try redesigning your code to make most of the shared data independent or immutable. This may not be possible (without making things too slow or too hard to understand), but think about it before making this decision.

If you have a lot of independent data or common immutable data, threads clearly benefit. Processes need either explicit sharing (e.g. multiprocessing.Array again) or marshaling. multiprocessing and its third-party alternatives make marshaling quite simple for simple cases where everything is matched, but it's still not as simple as passing values ​​directly, and also much slower.

Unfortunately, in most cases when you have a lot of immutable data to transfer, these are the same cases when you need a parallelism processor, which means that you have a compromise. And the best answer to this compromise may be OS threads on your current 4-core system, but you have processes in the 16-core system in 2 years. (If you arrange things like multiprocessing.ThreadPool or concurrent.futures.ThreadPoolExecutor and trivially switch to Pool or ProcessPoolExecutor later or even with a configuration switch at runtime, which pretty much solves the problem, but this is not always possible.)

Finally, if your application essentially requires an event loop (such as a GUI application or network server), first select the structure that you like. Encoding, say, PySide compared to wx , or twisted versus gevent , is a big difference than encoding using microprocessors and OS threads. And, once you have chosen the framework, see how much you can use in your event loop, where, in your opinion, you need real concurrency. For example, if you need some code to run every 30 seconds, don’t run a thread (micro or OS) for this, ask the framework to schedule it, however much it wants.

+5
source

All Articles