要理解 GIL 需要知道一些Python解释器的运行原理。Python的解释器其实一个循环,循环里是一系列的cases,逐行读取编译后的Opcode进行计算,每一个线程都有独立的这么一个循环。这里插一嘴,其实Python不是解释型语言,起码不是纯粹的解释型,Python源代码会被编译成Python自己的byte code,就是上面循环中的那些opcode。
/* BEWARE! It is essential that any operation that fails sets either x to NULL, err to nonzero, or why to anything but WHY_NOT, and that no operation that succeeds does this! */
# dictionary-like object that supports dot (attribute) syntax gil = SimpleNamespace( drop_request=False, locked=True, switch_number=0, last_holder=None, eval_breaker=True )
defdrop_gil(thread_id): ifnot gil.locked: raise Exception("GIL is not locked")
gil_mutex.acquire()
gil.last_holder = thread_id gil.locked = False
# Signals that the GIL is now available for acquiring to the first awaiting thread gil_condition.notify()
gil_mutex.release()
# force switching # Lock current thread so it will not immediately reacquire the GIL # this ensures that another GIL-awaiting thread have a chance to get scheduled
if gil.drop_request: switch_condition.acquire() if gil.last_holder == thread_id: gil.drop_request = False switch_condition.wait()
switch_condition.release()
deftake_gil(thread_id): gil_mutex.acquire()
while gil.locked: saved_switchnum = gil.switch_number
# Release the lock and wait for a signal from a GIL holding thread, # set drop_request=True if the wait is timed out
timed_out = not gil_condition.wait(timeout=DEFAULT_INTERVAL)
if timed_out and gil.locked and gil.switch_number == saved_switchnum: gil.drop_request = True
# lock for force switching switch_condition.acquire()
# Now we hold the GIL gil.locked = True
if gil.last_holder != thread_id: gil.last_holder = thread_id gil.switch_number += 1
# force switching, send signal to drop_gil switch_condition.notify() switch_condition.release()
if gil.drop_request: gil.drop_request = False
gil_mutex.release()
defexecution_loop(target_function, thread_id): # Compile Python function down to bytecode and execute it in the while loop
bytecode = compile(target_function)
whileTrue:
# drop_request indicates that one or more threads are awaiting for the GIL if gil.drop_request: # release the gil from the current thread drop_gil(thread_id)
# immediately request the GIL for the current thread # at this point the thread will be waiting for GIL and suspended until the function return take_gil(thread_id)
# bytecode execution logic, executes one instruction at a time instruction = bytecode.next_instruction() if instruction isnotNone: execute_opcode(instruction) else: return