- In these diagrams, the Python interpreter scale lines are shown along the X-axis.
- The two bars represent two different threads executing.
- The white area represents the time when the thread is completely idle.
- The green area indicates when the thread is holding the GIL and running.
- The red area indicates when the operating system only scheduled threads to wake it up and found the GIL unavailable.
- See the following legend for details:
- First, this is the behavior of running two CPU-bound threads on a single CPU system.
- Over a long period of time, threads alternate nicely.
- See the following legend for details:
- Second, dual-core laptop machine startup code.
- All of these red areas indicate that the operating system has scheduled Python threads on one kernel, but cannot run because a thread on another kernel holds that thread.
- Involves an I/O bound thread competing with a CPU bound thread. In this example, the I/O thread returns only UDP packets. This is the code for this thread.
Def thread_1: s = socket (AF_INET, SOCK_DGRAM) s.bind ((" ", port)) and True: MSG, addr = s.recvfrom (1024) s.sinto (MSG, addr)Copy the code
- The other thread (thread 2) is just spinning mindlessly. This figure shows what happens when a UDP message is sent to thread 1.
- As you might expect, most of the time is spent running cpu-bound threads. However, when I/O is received, a series of activities occur in the I/O thread. Let’s zoom in on the area and see what’s happening.
- In this figure, you will see the difficulty of binding I/O to the GIL for a small amount of processing.
- Approximately 17,000 interpreter clicks passed between the arrival of the UDP message and the successful return of the s.revfrom () call (and note all GIL contention)
- More than 34,000 ticks are passed between s.sendto() and the loop returning the next s.recvfrom().
The simple conclusion is that Python’s multithreading has a positive effect only on I/O intensive computing on multi-core cpus; However, when there is at least one CPU-intensive thread, the multithreading efficiency will be significantly reduced due to the GIL.
- Speed test
import time
COUNT = 10000000
start = time.clock()
def countdown(n):
while n > 0:
n -= 1
countdown(COUNT)
print("Time used:",(time.clock() - start))
# ('Time used:', 0.392953)
import time
from threading import Thread
COUNT = 10000000
start = time.clock()
def countdown(n):
while n > 0:
n -= 1
t1 = Thread(target=countdown, args=[COUNT // 2])
t2 = Thread(target=countdown, args=[COUNT // 2])
t1.start()
t2.start()
t1.join()
t2.join()
print("Time used:",(time.clock() - start))
# ('Time used:', 0.910045)
Copy the code
That’s all!