Anyone who’s ever had the dubious pleasure of living in shared accommodation will appreciate the intrinsic value of the TV remote control and the power it gives the holder. The person with the remote, in spite of the obvious unfairness, can decide what his flatmates watch.
Everyone else is on the edge of their seat waiting for it to be released. And when the incumbent holder leaves the room, he triggers a race for the device and occasionally the start of a lifelong grudge.
This parallels the competition between threads of code in a Python script competing for the Global Interpreter Lock. Possessing the lock allows the thread to access the processor core and execute instructions.
What is a Global Interpreter Lock
A Global Interpretation Lock (GIL) is a device used in the Python interpreter to lock down the execution of threads in the processor. This ensures that only one thread is executed at any one time, preventing errors if the underlying code isn’t thread safe. Thread safety is critically important.
GIL operates by forcing the thread to acquire the lock before it can access the object but it can only acquire the lock after it’s been released by the previous thread. This is the kernel of the GIL’s problem, according to David Beazley who is probably the foremost researcher into the effects of GIL. The thread that’s just given the lock up is in pole position in the race to grab the freed lock back again and in many cases this is exactly what happens.
The previous incumbent can grab the lock again and continue on. In Python CPU bound threads can hog the lock at the expense of I/O threads. Number crunching or processing images or audio are problematic here because they’ll keep the lock for prolonged periods and impede performance. GIL has a “tick” mechanism that forces threads to give up the lock if it’s running for too long. Python 3.2 has abandoned the “tick” mechanism and replaced it by a timer.
Now after 5ms the thread can be forced to give up the lock. The GIL essentially protects the programme and it works in all processors even multicore ones. You can read Dave Beazley’s “Understanding GIL” PDF here.
In this short (5min) video clip, Dave explains why he started using Python.
This next video is longer at 52 minutes and is a copy of Dave Beazley’s presentation from RuPy’11 in Poznan, Poland back in 2011.
Global Interpretation Locks make maintaining Python interpreters easier. But it is a solution that can create its own problems. It can stop multi-threaded Python programmes from fully optimising multi-processing technology.
The GIL being global is a one-size-fits-all mechanism for synchronizing threads. There are other locking methods. Using C extensions can allow the lock to be released and picked up by another thread while the task is executed in the C code. Another alternative is a lock for each individual data structure, which can protect the data and allow threads to interact if they’re not sharing a dataset. .
This allows parallelism but the increase in work rate with the management of possibly more than one lock for every line of code will slow the operation down and this is the main reason why attempts to use this method in replacing the GIL have not really taken off.
And that’s the problem of getting around the GIL’s limitations, alternatives make the operations even slower. Attempts to replace GIL haven’t as yet been successful. Any viable alternative has to be simple to maintain and robust. It can’t increase overheads.
And also it has to support concurrency and at least effectively manage the errors that the GIL was designed to prevent. Additionally, Python programmers can create priority schedules. But this is not without its complications.
GIL is the worst thread management device, apart from all the alternatives to date. It looks set to be a constant headache for developers at least in the middle-term but one way of looking at GIL’s trials and tribulations is that it builds character.