When a new process or program begins running, it will start with just one thread, which is called the main thread because it's the main one that runs when the program begins.
That main thread can then start or spawn additional threads to help out, referred to as its child threads, which are part of the same process but execute independently to do other tasks
After finishing doing tasks child threads can be terminated, and the main thread can continue running.
The main thread is usually the last to finish the execution of the program, and when it does, the process will end.
There are 5 states of threads:
- New - just created
- Runnable - ready to run
- Running - currently running
- Blocked - waiting for something to happen
- Terminated - finished running
We can use join() to wait for another thread to finish
In this case the Thread-1 will be in the blocked state until the Thread-2 finishes
There are two common approaches to managing threads:
import threading, time
class ChefOlivia(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
print('Olivia started & waiting for sausage to thaw...')
time.sleep(3)
print('Olivia is done cutting sausage.')
# main thread
if __name__ == '__main__':
print("Barron started & requesting Olivia's help.")
olivia = ChefOlivia()
print(' Olivia alive?:', olivia.is_alive())
print('Barron tells Olivia to start.')
olivia.start()
print(' Olivia alive?:', olivia.is_alive())
print('Barron continues cooking soup.')
time.sleep(0.5)
print(' Olivia alive?:', olivia.is_alive())
print('Barron patiently waits for Olivia to finish and join...')
olivia.join()
print(' Olivia alive?:', olivia.is_alive())
print('Barron and Olivia are both done!')
Barron started & requesting Olivia's help.
Olivia alive?: False
Barron tells Olivia to start.
Olivia started & waiting for sausage to thaw...
Olivia alive?: True
Barron continues cooking soup.
Olivia alive?: True
Barron patiently waits for Olivia to finish and join...
Olivia is done cutting sausage.
Olivia alive?: False
Barron and Olivia are both done!
Process finished with exit code 0
Threads that run in the background are called daemon threads. Daemon threads are not critical to the main execution of the program, and their failure to complete does not impact the program's execution.
If the main thread terminates, all daemon threads are abruptly stopped without completing their work.
If the main thread is still running, the program will not terminate until all non-daemon threads have completed.
If a daemon thread in Python creates another thread, that child thread will also be a daemon thread.
The daemon thread will not prevent the program from terminating when the main thread is finished.
It's perfect for background tasks.
- Why would ThreadA call the ThreadB.join() method?
Answer: ThreadA needs to wait until after ThreadB has terminated to continue.
- A thread that calls the join method on another thread will enter the _____ state until the other thread finishes executing.
Answer: Blocked
- Why do you have to start a thread after creating it?
Answer: Threads do not automatically run when instantiated.
- You can safely expect threads to execute in the same relative order that you create them.
Answer: False
- For a Python application to use multiple processor cores for parallel execution, it must be structured to have multiple _____.
Answer: Processes
- A hyperthreaded processor with eight logical cores will usually provide _____ performance compared to a regular processor with eight physical cores.
Answer: lower
- The operating system assigns each process a unique _____.
Answer: Process ID
- The Global Interpreter Lock has a significant, negative impact on the performance of multi-threaded, I/O-bound programs.
Answer: False
I/O-bound tasks spend most of their time waiting on external actions, like network operations or user input. They do not need to possess the GIL while waiting on I/O, so the GIL usually has a minimal impact on I/O-intensive applications.
- The Global Interpreter Lock prevents multiple Python _____ from executing at the same time.
Answer: threads
- It is possible for two tasks to execute _____ using a single-core processor.
Answer: concurrently (Concurrent tasks can take turns to execute on the same processor.)
- Concurrent tasks always execute at the same time.
Answer: False
Concurrency describes the structure that enables a program to execute in parallel (given the necessary hardware), but a concurrent program is not inherently parallel.
- Every thread is independent and has its own separate address space in memory.
Answer: False
Threads share the same address space in memory, so they can access the same variables and data structures.