11

We're all aware of the horrors of the GIL, and I've seen a lot of discussion about the right time to use the multiprocessing module, but I still don't feel that I have a good intuition about when threading in Python (focusing mainly on CPython) is the right answer.

What are instances in which the GIL is not a significant bottleneck? What are the types of use cases where threading is the most appropriate answer?

mvanveen
  • 9,754
  • 8
  • 33
  • 42

4 Answers4

14

Threading really only makes sense if you have a lot of blocking I/O going on. If that's the case, then some threads can sleep while other threads work. If threads are CPU-bound, you're not likely to see much benefit from multithreading.

Note that the multiprocessing module, while more difficult to code for, makes use of separate processes and therefore doesn't suffer the downsides of the GIL.

Michael Mior
  • 28,107
  • 9
  • 89
  • 113
  • I agree with Michael answer. Threads are enough for example for simple network operations, gathering input from user or updating GUI forms while doing some work in background. I use it quite often for such tasks. What I want to add for this answer is my experience with multiprocessing module. I almost doesn't use this module. Every time I want to use it that means I need full CPU power for a longer period of time. So I start thinking how to optimize this and finally I end up writing C/C++ module for python. In this C/C++ module I place all critical multithread code. – Zuljin Jan 24 '12 at 21:55
  • @mklauber Even on multicore machines, only one thread can execute at at time. This is a consequence of the Global Interpreter Lock (GIL) in CPython. – Michael Mior Jan 24 '12 at 22:02
  • @Michael Mior, you're absolutely correct, I completely confused the GIL. Editing/deleting the incorrect comment now. – mklauber Jan 24 '12 at 22:05
  • Is it reasonable to think there are cases when a set of io-bound tasks are better suited for `multiprocessing` over threading? I would think that there could be instances where the overhead of a context switch is not so bad in comparison to the overhead caused by the GIL. – mvanveen Jan 25 '12 at 06:46
  • 1
    @mvanveen If you have a large number of I/O bound threads where several will be unblocked at any particular time, then yes. Really, the best way to find out is to measure :) – Michael Mior Jan 25 '12 at 12:53
10

Since you seem to be looking for examples, here are some off the top of my head and grabbed from searching for CPU-bound and I/O-bound examples (I can't seem to find many). I am no expert, so please feel free to correct anything I've miscategorized. It's also worth noting that advancing technology could move a problem from one category to another.

CPU Bound Tasks (use multiprocessing)

  • Numerical methods/approximations for mathematical functions (calculating digits of pi, etc.)
  • Image processing
  • Performing convolutions
  • Calculating transforms for graphics programming (possibly handled by GPU)
  • Audio/video compression/decompression

I/O Bound Tasks (threading is probably OK)

  • Sending data across a network
  • Writing to/reading from the disk
  • Asking for user input
  • Audio/video streaming
aganders3
  • 5,838
  • 26
  • 30
2

The GIL prevents python from running multiple threads.

If your code releases the GIL before jumping into a C extension, other python threads can continue while the C code runs. Like with the blocking IO, that other people have mentioned.

Ctypes does this automatically, and so does numpy. So if your code uses them a lot, it may not be significantly restricted by the GIL.

mdaoust
  • 6,242
  • 3
  • 28
  • 29
0

Besides the CPU bound and I/O bound tasks, there is still more use cases. For example, thread enables concurrent tasks. A lot of GUI programming fall into this category. The main loop have to be responsive to mouse events. So anytime you have a task that take a while and you don't want to freeze the UI, you do it on a separate thread. It is less about performance and more about parallelism.

Wai Yip Tung
  • 18,106
  • 10
  • 43
  • 47