15

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?

4
  • 1
    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). Commented May 15, 2020 at 0:58
  • @martineau if that is true, where does the "wait" occur? Commented May 15, 2020 at 1:00
  • I think the threads end up waiting in parallel. Commented May 15, 2020 at 1:03
  • 1
    A sleeping thread isn't executing. 5 threads sleeping at once aren't executing at once. Commented May 15, 2020 at 1:08

2 Answers 2

16

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

Sign up to request clarification or add additional context in comments.

1 Comment

Very interesting to include CPython source code here, will give it a good read cheers.
10

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.