Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • 📝 Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • 📝 Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • 📝 Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • 📝 Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • 📝 While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • 📝 For Each Assignments
    • 📝 All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • 📝 Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • 📝 Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • 📝 Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • 📝 Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • 📝 Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • 📝 Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • 📝 Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • 📝 Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • 📝 Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • 📝 Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • 📝 Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • 📝 Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • 📝 Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • 📝 Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • 📝 Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • 📝 Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • 📝 Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • 📝 Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • 📝 Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Creating Threads

Python Creating Threads - Complete Guide

Learn different ways to create and manage threads in Python.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
Read Time: 10 minutes Level: Intermediate Topic: Python Concurrency Focus: Thread Creation & Management
On this page:
  • What is Thread Creation?
  • The Threading Module
  • Creating Threads with Functions
  • Creating Threads with Classes
  • Thread Pools
  • Daemon Threads
  • Thread Lifecycle
  • Best Practices
  • Try It Yourself
  • Quiz
  • FAQ
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • What is thread creation — spawning new threads
  • The threading module — Python's thread library
  • Creating threads with functions — target-based approach
  • Creating threads with classes — subclassing Thread
  • Thread pools — managing multiple threads
  • Daemon threads — background vs foreground
  • Thread lifecycle — from creation to termination

What is Thread Creation?

Thread creation is the process of spawning new threads in your Python program. Each thread runs independently and can execute tasks concurrently, making your program more efficient and responsive.

👨‍💼 Think of it like hiring employees.

You (the main thread) can hire employees (create threads) to handle different tasks. Each employee works independently, and you can manage them by assigning tasks, monitoring progress, and waiting for them to finish.

📝 Function-based

Create threads using target functions

📦 Class-based

Subclass Thread for custom behavior

🏊 Thread Pools

Manage multiple threads efficiently

⚙️ Daemon Threads

Background threads that auto-terminate

💡 Key concept: The threading module provides all the tools you need to create and manage threads in Python.

The Threading Module

1

Python's Threading Module

The threading module is Python's built-in library for thread creation and management. It provides high-level thread APIs and is the recommended way to work with threads.

# ============================================================
# INTRODUCTION TO THREADING MODULE
# ============================================================

import threading
import time

print("=" * 40)
print("THREADING MODULE BASICS")
print("=" * 40)

# ============================================================
# CHECKING THREAD INFORMATION
# ============================================================

print(f"Main thread name: {threading.main_thread().name}")
print(f"Active threads count: {threading.active_count()}")
print(f"Current thread name: {threading.current_thread().name}")
print(f"Current thread ident: {threading.get_ident()}")

print("\nActive threads:")
for t in threading.enumerate():
    print(f"  - {t.name} (alive: {t.is_alive()})")

# ============================================================
# SIMPLE THREAD CREATION
# ============================================================

def worker():
    """Simple worker function"""
    thread_name = threading.current_thread().name
    print(f"  {thread_name}: Worker started")
    time.sleep(1)
    print(f"  {thread_name}: Worker finished")

print("\n" + "=" * 40)
print("CREATING A THREAD")
print("=" * 40)

# Create a thread
t = threading.Thread(target=worker, name="WorkerThread")
print(f"Created thread: {t.name}")

# Start the thread
t.start()
print(f"Thread {t.name} started")

# Wait for thread to finish
t.join()
print("Thread completed!")

print("""
┌─────────────────────────────────────────────────────────────────┐
│ THREADING MODULE KEY FEATURES:                                │
├─────────────────────────────────────────────────────────────────┤
│ • Thread class for creating threads                         │
│ • active_count() - number of active threads                 │
│ • current_thread() - get current thread                    │
│ • enumerate() - list all active threads                    │
│ • main_thread() - get main thread                          │
└─────────────────────────────────────────────────────────────────┘
""")

Threading module key points:

  • Thread class — main API for thread creation
  • Active threads — track running threads
  • Thread identification — name and ident
  • Built-in — no external dependencies

Quick Check: What module provides thread support in Python? (Answer: the threading module)

Creating Threads with Functions

2

Threads with Target Functions

The most common way to create a thread is by passing a function to the Thread constructor. The function will run in the new thread when start() is called.

# ============================================================
# CREATING THREADS WITH FUNCTIONS
# ============================================================

import threading
import time
import random

print("=" * 40)
print("FUNCTION-BASED THREADS")
print("=" * 40)

# ============================================================
# BASIC FUNCTION THREAD
# ============================================================

def greet(name, delay=1):
    """Simple function to run in a thread"""
    print(f"Hello, {name}! Thread starting...")
    time.sleep(delay)
    print(f"Goodbye, {name}! Thread ending.")

print("\n1. Basic thread with function:")
t = threading.Thread(target=greet, args=("Alice", 0.5))
t.start()
t.join()

# ============================================================
# THREAD WITH MULTIPLE ARGUMENTS
# ============================================================

def process_data(data_id, count, delay=0.3):
    """Process data with multiple arguments"""
    thread_name = threading.current_thread().name
    print(f"[{thread_name}] Processing data {data_id} ({count} items)")
    
    for i in range(count):
        time.sleep(delay)
        print(f"[{thread_name}]   Item {i+1}/{count} processed")
    
    print(f"[{thread_name}] Data {data_id} complete!")

print("\n2. Thread with multiple arguments:")
t = threading.Thread(
    target=process_data,
    args=(101, 3),
    kwargs={"delay": 0.2},
    name="DataProcessor"
)
t.start()
t.join()

# ============================================================
# MULTIPLE THREADS WITH SAME FUNCTION
# ============================================================

def task(task_id):
    """Worker task with random sleep"""
    duration = random.uniform(0.3, 0.8)
    print(f"  Task {task_id}: started (duration: {duration:.2f}s)")
    time.sleep(duration)
    print(f"  Task {task_id}: completed!")

print("\n3. Multiple threads with same function:")
threads = []
for i in range(5):
    t = threading.Thread(target=task, args=(i,), name=f"Worker-{i}")
    threads.append(t)
    t.start()

print("All threads started, waiting for completion...")
for t in threads:
    t.join()
print("All tasks completed!")

# ============================================================
# LAMBDA FUNCTIONS IN THREADS
# ============================================================

print("\n4. Lambda function in thread:")
t = threading.Thread(
    target=lambda: print(f"  Lambda thread {threading.current_thread().name}: Hello!"),
    name="LambdaThread"
)
t.start()
t.join()

print("""
┌─────────────────────────────────────────────────────────────────┐
│ FUNCTION-BASED THREAD ADVANTAGES:                             │
├─────────────────────────────────────────────────────────────────┤
│ • Simple and straightforward                                  │
│ • Reusable functions                                          │
│ • Easy to pass arguments                                      │
│ • Works with lambdas                                         │
├─────────────────────────────────────────────────────────────────┤
│ FUNCTION-BASED THREAD DISADVANTAGES:                         │
├─────────────────────────────────────────────────────────────────┤
│ • Limited thread-specific customization                      │
│ • Cannot override Thread methods                             │
│ • Less control over thread lifecycle                         │
└─────────────────────────────────────────────────────────────────┘
""")

Function-based creation key points:

  • Target function — the function to run
  • Arguments — pass via args and kwargs
  • Thread name — optional naming
  • Simple — quick and easy to implement

Quick Check: How do you pass arguments to a thread's target function? (Answer: Using the args and kwargs parameters)

Creating Threads with Classes

3

Threads by Subclassing Thread

You can create threads by subclassing the Thread class and overriding the run() method. This gives you more control and allows for custom thread-specific behavior.

# ============================================================
# CREATING THREADS WITH CLASSES
# ============================================================

import threading
import time
import random

print("=" * 40)
print("CLASS-BASED THREADS")
print("=" * 40)

# ============================================================
# BASIC THREAD SUBCLASS
# ============================================================

class MyThread(threading.Thread):
    """Custom thread class"""
    
    def __init__(self, name, delay=1):
        super().__init__(name=name)
        self.delay = delay
    
    def run(self):
        """Override the run method"""
        print(f"  {self.name}: Starting (delay: {self.delay}s)")
        time.sleep(self.delay)
        print(f"  {self.name}: Finishing")

print("\n1. Basic thread subclass:")
t = MyThread("CustomThread", delay=0.5)
t.start()
t.join()

# ============================================================
# ADVANCED THREAD SUBCLASS WITH DATA
# ============================================================

class DataProcessor(threading.Thread):
    """Thread that processes data"""
    
    def __init__(self, data_id, items, name=None):
        super().__init__(name=name)
        self.data_id = data_id
        self.items = items
        self.result = None
    
    def run(self):
        """Process data and store result"""
        print(f"[{self.name}] Processing data {self.data_id}")
        total = 0
        for i, item in enumerate(self.items):
            time.sleep(0.1)
            total += item
            print(f"[{self.name}]   Item {i+1}/{len(self.items)}: {item}")
        
        self.result = total
        print(f"[{self.name}] Data {self.data_id} complete! Total: {total}")

print("\n2. Advanced thread subclass with data:")
processor = DataProcessor(
    data_id=123,
    items=[10, 20, 30, 40, 50],
    name="DataProcessor"
)
processor.start()
processor.join()
print(f"Result: {processor.result}")

# ============================================================
# THREAD WITH ERROR HANDLING
# ============================================================

class SafeThread(threading.Thread):
    """Thread with built-in error handling"""
    
    def __init__(self, target, args=(), kwargs=None, name=None):
        super().__init__(name=name)
        self.target = target
        self.args = args
        self.kwargs = kwargs or {}
        self.exception = None
    
    def run(self):
        """Run with error handling"""
        try:
            self.target(*self.args, **self.kwargs)
        except Exception as e:
            self.exception = e
            print(f"[{self.name}] Error: {e}")

def faulty_function():
    """Function that raises an error"""
    time.sleep(0.2)
    raise ValueError("Something went wrong!")

print("\n3. Thread with error handling:")
safe_thread = SafeThread(
    target=faulty_function,
    name="SafeThread"
)
safe_thread.start()
safe_thread.join()

if safe_thread.exception:
    print(f"Caught exception: {safe_thread.exception}")
else:
    print("Thread completed successfully!")

print("""
┌─────────────────────────────────────────────────────────────────┐
│ CLASS-BASED THREAD ADVANTAGES:                                │
├─────────────────────────────────────────────────────────────────┤
│ • More control and customization                              │
│ • Can store thread-specific data                             │
│ • Override methods (run, __init__, etc.)                    │
│ • Better encapsulation                                       │
├─────────────────────────────────────────────────────────────────┤
│ CLASS-BASED THREAD DISADVANTAGES:                            │
├─────────────────────────────────────────────────────────────────┤
│ • More verbose than function-based                           │
│ • Slightly more complex                                      │
│ • Overkill for simple tasks                                 │
└─────────────────────────────────────────────────────────────────┘
""")

Class-based creation key points:

  • Subclass Thread — inherit from threading.Thread
  • Override run() — define thread behavior
  • Custom attributes — store thread-specific data
  • Encapsulation — better organization

Quick Check: What method must you override when subclassing Thread? (Answer: the run() method)

Thread Pools

4

Managing Threads with Pools

A thread pool manages a collection of worker threads, making it easy to execute many tasks without creating a new thread for each one. This is more efficient and scalable.

# ============================================================
# THREAD POOLS WITH ThreadPoolExecutor
# ============================================================

from concurrent.futures import ThreadPoolExecutor
import threading
import time
import random

print("=" * 40)
print("THREAD POOLS")
print("=" * 40)

# ============================================================
# BASIC THREAD POOL
# ============================================================

def worker(task_id):
    """Worker function for thread pool"""
    duration = random.uniform(0.2, 0.6)
    thread_name = threading.current_thread().name
    print(f"  [{thread_name}] Task {task_id}: started ({duration:.2f}s)")
    time.sleep(duration)
    result = f"Task {task_id} result"
    print(f"  [{thread_name}] Task {task_id}: completed")
    return result

print("\n1. Thread pool with 3 workers:")
with ThreadPoolExecutor(max_workers=3) as executor:
    # Submit tasks and get futures
    futures = [executor.submit(worker, i) for i in range(6)]
    
    # Get results as they complete
    for future in futures:
        result = future.result()
        print(f"  Got result: {result}")

# ============================================================
# MAP WITH THREAD POOL
# ============================================================

def square(n):
    """Compute square"""
    time.sleep(0.1)
    return n * n

print("\n2. Using map() with thread pool:")
with ThreadPoolExecutor(max_workers=4) as executor:
    results = executor.map(square, range(10))
    print(f"  Squares: {list(results)}")

# ============================================================
# ASYNCHRONOUS CALLBACKS
# ============================================================

def callback(future):
    """Callback function"""
    print(f"  Callback received: {future.result()}")

print("\n3. Asynchronous callbacks:")
with ThreadPoolExecutor(max_workers=2) as executor:
    future = executor.submit(worker, 999)
    future.add_done_callback(callback)
    print("  Waiting for callback...")
    time.sleep(1)

# ============================================================
# POOL WITH MULTIPLE ARGUMENTS
# ============================================================

def process_with_args(data, multiplier, offset=0):
    """Process with multiple arguments"""
    time.sleep(0.05)
    return data * multiplier + offset

print("\n4. Thread pool with multiple arguments:")
with ThreadPoolExecutor(max_workers=3) as executor:
    # Submit with different arguments
    futures = [
        executor.submit(process_with_args, i, 10, offset=5)
        for i in [1, 2, 3, 4, 5]
    ]
    results = [f.result() for f in futures]
    print(f"  Results: {results}")

print("""
┌─────────────────────────────────────────────────────────────────┐
│ THREAD POOL ADVANTAGES:                                       │
├─────────────────────────────────────────────────────────────────┤
│ • Reuses threads (efficient)                                 │
│ • Limits concurrent threads                                  │
│ • Easy task submission                                       │
│ • Built-in result handling                                  │
├─────────────────────────────────────────────────────────────────┤
│ THREAD POOL DISADVANTAGES:                                   │
├─────────────────────────────────────────────────────────────────┤
│ • Less control over individual threads                       │
│ • Threads are shared                                         │
│ • May block if all threads busy                             │
└─────────────────────────────────────────────────────────────────┘
""")

Thread pool key points:

  • Reuse — threads are reused for multiple tasks
  • Limited concurrency — max_workers controls
  • Future objects — handle results asynchronously
  • map() — easy parallel mapping

Quick Check: What is the benefit of using a thread pool? (Answer: Threads are reused, making it more efficient)

Daemon Threads

5

Background vs Foreground Threads

Daemon threads run in the background and automatically terminate when the main program exits. Non-daemon (foreground) threads keep the program running until they finish.

# ============================================================
# DAEMON THREADS
# ============================================================

import threading
import time

print("=" * 40)
print("DAEMON THREADS")
print("=" * 40)

# ============================================================
# NON-DAEMON THREAD
# ============================================================

def non_daemon_task():
    """Non-daemon thread that keeps running"""
    print("  [Non-Daemon] Task starting...")
    for i in range(3):
        time.sleep(0.3)
        print(f"  [Non-Daemon] Working... {i+1}/3")
    print("  [Non-Daemon] Task complete!")

print("\n1. Non-daemon thread:")
nd_thread = threading.Thread(target=non_daemon_task, name="NonDaemon")
nd_thread.start()

# ============================================================
# DAEMON THREAD
# ============================================================

def daemon_task():
    """Daemon thread that will be terminated"""
    print("  [Daemon] Task starting...")
    for i in range(5):
        time.sleep(0.2)
        print(f"  [Daemon] Still running... {i+1}/5")
    print("  [Daemon] Task complete! (may not execute)")

print("\n2. Daemon thread:")
d_thread = threading.Thread(target=daemon_task, name="Daemon")
d_thread.daemon = True  # Set as daemon
d_thread.start()

# Wait for non-daemon to finish
time.sleep(1.5)
print("\nMain program ending...")
print("Daemon thread will be terminated automatically.")

# ============================================================
# CHECKING DAEMON STATUS
# ============================================================

def daemon_checker(name):
    """Check daemon status"""
    t = threading.current_thread()
    print(f"  {name}: is_daemon = {t.daemon}")

print("\n3. Checking daemon status:")
# Create threads with different daemon settings
threads = [
    threading.Thread(target=daemon_checker, args=("T1",), daemon=True),
    threading.Thread(target=daemon_checker, args=("T2",), daemon=False),
]
for t in threads:
    t.start()
for t in threads:
    t.join()

# ============================================================
# USE CASE: LOGGING DAEMON
# ============================================================

class LoggingThread(threading.Thread):
    """Daemon thread for background logging"""
    
    def __init__(self, log_file):
        super().__init__(daemon=True)
        self.log_file = log_file
        self.running = True
    
    def run(self):
        count = 0
        while self.running and count < 5:
            time.sleep(0.5)
            count += 1
            with open(self.log_file, 'a') as f:
                f.write(f"Log entry {count}\n")
            print(f"  [Logger] Written log {count}")
        print("  [Logger] Stopping...")

print("\n4. Daemon logger thread:")
logger = LoggingThread("daemon.log")
logger.start()
time.sleep(2.5)
print("Main program ending, logger will terminate...")

print("""
┌─────────────────────────────────────────────────────────────────┐
│ DAEMON THREAD KEY POINTS:                                     │
├─────────────────────────────────────────────────────────────────┤
│ • Background threads                                          │
│ • Terminate when main program exits                          │
│ • Set with `thread.daemon = True`                           │
│ • Used for background tasks (logging, monitoring)           │
│ • Use with caution (may exit mid-task)                     │
└─────────────────────────────────────────────────────────────────┘
""")

Daemon thread key points:

  • Background — run in the background
  • Auto-terminate — exit when main exits
  • setDaemon() — mark as daemon
  • Use cases — logging, monitoring, cleanup

Quick Check: What happens to a daemon thread when the main program exits? (Answer: It terminates automatically)

Thread Lifecycle

6

Understanding Thread States

A thread goes through several states during its lifetime: creation, ready, running, blocked, and terminated. Understanding these states helps you manage threads effectively.

# ============================================================
# THREAD LIFECYCLE DEMONSTRATION
# ============================================================

import threading
import time
import random

print("=" * 40)
print("THREAD LIFECYCLE")
print("=" * 40)

# ============================================================
# LIFECYCLE TRACKING
# ============================================================

class LifecycleThread(threading.Thread):
    """Thread that tracks its own lifecycle"""
    
    def __init__(self, name):
        super().__init__(name=name)
        self.state = "created"
        print(f"  [{self.name}] State: {self.state}")
    
    def run(self):
        self.state = "running"
        print(f"  [{self.name}] State: {self.state}")
        
        # Simulate work
        duration = random.uniform(0.5, 1.5)
        print(f"  [{self.name}] Working for {duration:.2f}s...")
        time.sleep(duration)
        
        self.state = "finished"
        print(f"  [{self.name}] State: {self.state}")

print("\n1. Thread lifecycle tracking:")
t = LifecycleThread("LifecycleDemo")
print(f"  Before start - alive: {t.is_alive()}")

t.start()
print(f"  After start - alive: {t.is_alive()}")

t.join()
print(f"  After join - alive: {t.is_alive()}")

# ============================================================
# BLOCKED STATE DEMONSTRATION
# ============================================================

lock = threading.Lock()

def thread_with_lock(thread_id):
    """Thread that demonstrates blocked state"""
    print(f"  Thread {thread_id}: Waiting for lock...")
    with lock:
        print(f"  Thread {thread_id}: Lock acquired")
        time.sleep(1)
        print(f"  Thread {thread_id}: Lock released")

print("\n2. Blocked state demonstration:")
# Start first thread (acquires lock)
t1 = threading.Thread(target=thread_with_lock, args=(1,))
t1.start()

# Give t1 time to acquire lock
time.sleep(0.1)

# Start second thread (will block)
t2 = threading.Thread(target=thread_with_lock, args=(2,))
t2.start()

t1.join()
t2.join()

# ============================================================
# STATE CHECKING
# ============================================================

def state_checker():
    """Check thread states"""
    t = threading.current_thread()
    print(f"  {t.name}: alive={t.is_alive()}, daemon={t.daemon}")

print("\n3. State checking:")
t = threading.Thread(target=state_checker, name="StateChecker")
print(f"  Before start: alive={t.is_alive()}")
t.start()
t.join()
print(f"  After join: alive={t.is_alive()}")

print("""
┌─────────────────────────────────────────────────────────────────┐
│ THREAD LIFECYCLE STATES:                                      │
├─────────────────────────────────────────────────────────────────┤
│ 1. CREATED - Thread object created but not started           │
│ 2. READY - Thread is ready to run (start() called)          │
│ 3. RUNNING - Thread is actively executing                   │
│ 4. BLOCKED - Thread is waiting (sleep, lock, I/O)          │
│ 5. TERMINATED - Thread has finished execution               │
├─────────────────────────────────────────────────────────────────┤
│ IMPORTANT METHODS:                                            │
├─────────────────────────────────────────────────────────────────┤
│ • is_alive() - Check if thread is running                   │
│ • start() - Start thread execution                          │
│ • join() - Wait for thread to finish                       │
│ • name - Thread name property                               │
└─────────────────────────────────────────────────────────────────┘
""")

Thread lifecycle key points:

  • Created — thread object exists
  • Running — actively executing
  • Blocked — waiting for something
  • Terminated — finished execution

Quick Check: What method checks if a thread is still running? (Answer: is_alive())

Best Practices

7

Thread Creation Best Practices

# ============================================================
# THREAD CREATION BEST PRACTICES
# ============================================================

print("1. USE APPROPRIATE CREATION METHOD")
print("   - Function-based: Simple tasks, quick implementation")
print("   - Class-based: Complex threads with custom behavior")
print("   - Thread pools: Many similar tasks")

print("\n2. NAME YOUR THREADS")
print("   - Helps with debugging")
print("   - thread = Thread(..., name='MyThread')")
print("   - thread.name = 'Worker'")

print("\n3. HANDLE THREAD EXCEPTIONS")
print("   - Catch exceptions within thread")
print("   - Use try/except in target function")
print("   - Consider SafeThread class pattern")

print("\n4. USE JOIN() APPROPRIATELY")
print("   - Call join() to wait for thread completion")
print("   - Use timeout: join(timeout=5)")
print("   - Avoid deadlocks")

print("\n5. CONSIDER DAEMON THREADS")
print("   - Set daemon=True for background tasks")
print("   - Use daemon threads for logging, monitoring")
print("   - Don't use daemon for critical tasks")

print("\n6. LIMIT THREAD COUNT")
print("   - Too many threads = overhead")
print("   - Use thread pools for many tasks")
print("   - Monitor with active_count()")

print("\n7. AVOID SHARED STATE")
print("   - Minimize shared variables")
print("   - Use thread-local data when possible")
print("   - Use synchronization when needed")

print("\n8. TEST THOROUGHLY")
print("   - Race conditions are hard to find")
print("   - Test with different thread counts")
print("   - Use stress testing")

Best practices summary:

  • Name threads — for debugging
  • Handle exceptions — thread errors
  • Use join() — wait for completion
  • Limit threads — avoid overhead

Quick Check: Why should you name your threads? (Answer: For debugging and identification)

Try It Yourself

Experiment with thread creation in the editor below.

Loading Pyodide... 0%
Python Code Editor
========================================
CREATING THREADS - PRACTICE
========================================

1. FUNCTION-BASED
----------------------------------------
Hello from Alice!
Goodbye from Alice!

2. CLASS-BASED
----------------------------------------
[Counter] Count: 1
[Counter] Count: 2
[Counter] Count: 3

3. MULTIPLE THREADS
----------------------------------------
Task 0: started
Task 1: started
Task 2: started
Task 0: done
Task 1: done
Task 2: done

All threads completed!
🏆

You've Got It!

You now understand how to create and manage threads in Python. You know function-based, class-based, thread pools, and daemon threads.

Quick Quiz

Test what you've learned:

1. What module is used for thread creation?
2. How do you pass arguments to a thread's target function?
3. What method must you override when subclassing Thread?
4. What is a daemon thread?
5. What is the benefit of using a thread pool?

Frequently Asked Questions

What is the difference between function-based and class-based thread creation? ▼

Function-based is simpler and quicker — just pass a function to Thread constructor. Class-based gives you more control — you can store thread-specific data, override methods, and have better encapsulation.

What is the difference between start() and run()? ▼

start() creates a new thread and calls run() in that new thread. run() is the method that contains the thread's code. You should never call run() directly — always use start().

Why should I use a thread pool? ▼

Thread pools reuse threads, which is more efficient than creating a new thread for each task. They also limit concurrency to prevent resource exhaustion and make it easier to manage many tasks.

What is a common interview question about creating threads? ▼

Common questions include: "How do you create a thread in Python?" "What is the difference between start() and run()?" "What is a daemon thread?" and "Explain the difference between function-based and class-based thread creation."

How many threads should I create? ▼

The optimal number depends on your hardware and tasks. As a general rule, use thread pools and let the executor manage the number. For CPU-bound tasks, use number of CPU cores. For I/O-bound tasks, you can use more threads.

Where to Go From Here

Now that you understand how to create threads, check out these related topics:

Introduction to Threads

Review the basics of threads and concurrency.

Review →

Multi Tasking

Learn about multi-tasking with threads.

Learn More →

Thread Synchronization

Learn how to synchronize threads safely.

Learn More →
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Uses of Threads Next: Single Tasking →