Threading, the GIL, multiprocessing, concurrent.futures, asyncio, and safe coordination of concurrent work.
Question 1: What is threading in Python?
Ans
Threading runs multiple threads within one process. It is useful for I/O-bound tasks where threads can make progress while other work waits.
Example
python
import threading
t = threading.Thread(target=lambda: print("Task"))
t.start()
t.join()
Important Point
Threads share process memory, so shared mutable state needs careful synchronization.
Question 2: What is the GIL?
Ans
In standard CPython builds, the Global Interpreter Lock historically allows only one thread at a time to execute Python bytecode in a process. It simplifies parts of memory management but limits CPU-bound parallelism with ordinary threads.
Example
python
import threading
print(threading.active_count())
Important Point
Modern Python has optional/free-threaded builds in newer releases; always state the interpreter/build assumption in advanced interviews.
Question 3: Threading vs multiprocessing?
Ans
Threading uses threads in one process and is often useful for I/O-bound work. Multiprocessing uses separate processes and can provide real CPU parallelism while avoiding a shared GIL between processes.
Example
python
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor() as pool:
print(list(pool.map(abs, [-2, -3])))
Important Point
Processes have higher communication and startup costs and require careful data sharing/serialization.
Question 4: What is concurrent.futures?
Ans
concurrent.futures provides high-level executors for running callables using threads or processes and collecting results through Future objects.
Example
python
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=2) as ex:
results = list(ex.map(str, [1, 2, 3]))
print(results)
Important Point
Choose ThreadPoolExecutor or ProcessPoolExecutor based on workload and runtime constraints.
Question 5: What is asyncio?
Ans
asyncio provides an event-loop-based model for cooperative asynchronous I/O using async functions, await, tasks, and asynchronous libraries.
Async code does not automatically make CPU-heavy work faster; it is primarily useful for concurrent I/O.
Question 6: What is a race condition?
Ans
A race condition occurs when program correctness depends on the timing of concurrent operations on shared state.
Example
python
# Two workers updating shared state without a proper synchronization strategy
Important Point
Use locks, thread-safe structures, message passing, or designs that minimize shared mutable state.
Question 7: What is Lock?
Ans
threading.Lock provides mutual exclusion so only one thread at a time can enter a protected critical section.
Example
python
import threading
lock = threading.Lock()
with lock:
# update shared state safely
pass
Important Point
Keep critical sections small to reduce contention and deadlock risk.
Question 8: What is a daemon thread?
Ans
A daemon thread is a background thread that does not keep the Python program alive once all non-daemon threads have finished.
Example
python
import threading
t = threading.Thread(target=lambda: None, daemon=True)
t.start()
Important Point
Do not use daemon threads when important work or cleanup must be guaranteed to finish.
Question 9: What is a process pool?
Ans
A process pool maintains reusable worker processes and distributes submitted tasks among them. It is useful for CPU-heavy independent tasks.
Example
python
from concurrent.futures import ProcessPoolExecutor
def square(x): return x*x
with ProcessPoolExecutor() as pool:
print(list(pool.map(square, [1,2,3])))
Important Point
Functions and arguments sent to worker processes generally need to be serializable by the chosen multiprocessing mechanism.