Why does time.sleep(...) not get affected by the GIL?
Asked Answered
S

2

13

From what I understood when doing research on the Python GIL, is that only one thread can be executed at the once (Whoever holds the lock). However, if that is true, then why would this code only take 3 seconds to execute, rather than 15 seconds?

import threading
import time

def worker():
    """thread worker function"""
    time.sleep(3)
    print 'Worker'

for i in range(5):
    t = threading.Thread(target=worker)
    t.start()

By intuition about threads, I would have thought this would take 3 seconds, which it does. However after learning about the GIL and that one thread can be executing at once, now I look at this code and think, why does it not take 15 seconds?

Striction answered 15/5, 2020 at 0:54 Comment(4)
I think sleep() does release the GIL. Also Python forces a thread switch every N millisecs or after executing M bytecode instructions (depending on which version of Python is being used).Osterman
@Osterman if that is true, where does the "wait" occur?Striction
I think the threads end up waiting in parallel.Osterman
A sleeping thread isn't executing. 5 threads sleeping at once aren't executing at once.Sailboat
K
14

Mario's answer is a good high level answer. If you're interested in some details of how this is implemented: in CPython, the implementation of time.sleep wraps its select system call with Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS:

https://github.com/python/cpython/blob/7ba1f75f3f02b4b50ac6d7e17d15e467afa36aac/Modules/timemodule.c#L1880-L1882

These are macros that save and restore the thread state, while releasing/acquiring the GIL:

https://github.com/python/cpython/blob/7c59d7c9860cdbaf4a9c26c9142aebd3259d046e/Include/ceval.h#L86-L94 https://github.com/python/cpython/blob/4c9ea093cd752a6687864674d34250653653f743/Python/ceval.c#L498

Kazan answered 15/5, 2020 at 1:4 Comment(1)
Very interesting to include CPython source code here, will give it a good read cheers.Striction
N
9

time.sleep(3) is not equivalent to a loop running for 3 seconds, burning CPU cycles while holding the GIL lock. The thread is switched away from for 3 seconds, allowing other threads to run.

See this stackoverflow question as to why time.sleep isn't a CPU intensive operation. To summarize, the thread is suspended until the timeout has elapsed.

Because all threads are sleeping, no real work on the CPU is being done, and the GIL lock doesn't prohibit these threads from working at the same time since no persistent lock was held to begin with.

The GIL lock comes into consideration when you have multiple threads doing CPU-intensive work. In that type scenario, you would see the slowdown you expect. The GIL does not cause any slowdowns in purely IO-bound work.

Nowhither answered 15/5, 2020 at 0:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.