杰瑞科技汇

python class thread

Of course! Let's dive deep into Python's threading module. This is a fundamental concept for writing concurrent applications.

python class thread-图1
(图片来源网络,侵删)

What is a Thread?

Think of a program as a single sequence of instructions. A thread is the smallest unit of execution within a process. A process can have multiple threads, all running in the same memory space.

Key Analogy:

  • Process: A factory. It has its own resources, machinery, and workspace.
  • Thread: A worker in the factory. Multiple workers can work in the same factory (share the same resources) on different tasks at the same time.

Why Use Threads?

  1. Concurrency: To perform multiple operations at once. For example, a web server can handle multiple client requests simultaneously.
  2. Responsiveness: In a graphical user interface (GUI), a long-running task (like downloading a file) can be run in a separate thread, preventing the entire application from freezing.
  3. Parallelism: On multi-core processors, multiple threads can truly run in parallel, each on a different core, speeding up CPU-bound tasks.

The threading Module in Python

Python provides a built-in threading module to create and manage threads. The two main ways to use it are:

python class thread-图2
(图片来源网络,侵删)
  1. By subclassing threading.Thread (more object-oriented)
  2. By passing a target function (more common and simpler)

Let's explore both.


Method 1: Subclassing threading.Thread

You create a new class that inherits from Thread and override the run() method. The code you want to execute in the new thread goes inside the run() method.

import threading
import time
# 1. Define a class that inherits from threading.Thread
class MyThread(threading.Thread):
    def __init__(self, name):
        # Call the constructor of the parent class
        super().__init__()
        self.name = name
    # 2. Override the run() method
    def run(self):
        print(f"Thread {self.name}: starting")
        time.sleep(2)  # Simulate a long-running task
        print(f"Thread {self.name}: finished")
# Create and start the threads
thread1 = MyThread("Alpha")
thread2 = MyThread("Beta")
print("Main     : starting threads")
# The start() method calls the run() method in a new thread
thread1.start()
thread2.start()
# The main program continues to execute
print("Main     : waiting for threads to complete")
# The join() method blocks the main thread until the thread has finished
thread1.join()
thread2.join()
print("Main     : all done")

Output (order might vary slightly):

Main     : starting threads
Thread Alpha: starting
Thread Beta: starting
Main     : waiting for threads to complete
Thread Alpha: finished
Thread Beta: finished
Main     : all done

Method 2: Passing a Target Function (More Common)

This approach is often simpler and more flexible. You create a Thread object and pass it a function to execute in its run() method.

python class thread-图3
(图片来源网络,侵删)
import threading
import time
# This is the function that will run in the new thread
def worker(num):
    """Thread worker function"""
    thread_name = threading.current_thread().name
    print(f"{thread_name}: Starting work on task {num}")
    time.sleep(2) # Simulate work
    print(f"{thread_name}: Finished task {num}")
# Create and start the threads
print("Main     : starting threads")
# Create a thread, passing the target function and its arguments
t1 = threading.Thread(target=worker, args=(1,), name="Worker-1")
t2 = threading.Thread(target=worker, args=(2,), name="Worker-2")
t1.start()
t2.start()
# The main program continues
print("Main     : waiting for threads to complete")
t1.join()
t2.join()
print("Main     : all done")

Output:

Main     : starting threads
Worker-1: Starting work on task 1
Worker-2: Starting work on task 2
Main     : waiting for threads to complete
Worker-1: Finished task 1
Worker-2: Finished task 2
Main     : all done

Key Methods and Properties of the Thread Class

Method/Property Description
start() Starts the thread by calling the run() method.
run() The method that contains the code for the thread. You override this in a subclass or pass a target function.
join([timeout]) | Blocks the calling thread until the thread whosejoin()method is called is terminated, or until the optionaltimeout` occurs. This is crucial for synchronization.
is_alive() Returns True if the thread is still running.
name A string name for the thread. You can set it in the constructor (name="MyThread") or access it.
ident A unique integer "thread identifier" for this thread.
daemon A boolean flag. If True, the thread is a "daemon thread". The main program will exit even if daemon threads are still running.

Synchronization: The Global Interpreter Lock (GIL)

This is the most important concept to understand about Python threading.

What is the GIL? The GIL is a mutex (a lock) that protects access to Python objects, preventing multiple native threads from executing Python bytecode at the same time within a single process.

What does this mean for you?

  • For CPU-bound tasks: The GIL prevents true parallelism on multi-core processors. Only one thread can execute Python code at a time. This makes threading a poor choice for tasks that are heavy on computation (e.g., complex math, video processing).
  • For I/O-bound tasks: The GIL is released when a thread is waiting for I/O (like reading a file, making a network request, or waiting for user input). This means while one thread is waiting, another thread can run. This makes threading excellent for I/O-bound tasks.

Example of a Race Condition (Why you need locks)

If multiple threads try to modify the same variable without synchronization, you can get a "race condition," leading to unpredictable and incorrect results.

import threading
# A shared variable
counter = 0
def increment_counter():
    global counter
    for _ in range(1_000_000):
        counter += 1 # This is NOT an atomic operation!
threads = []
for i in range(2):
    thread = threading.Thread(target=increment_counter)
    threads.append(thread)
    thread.start()
for thread in threads:
    thread.join()
print(f"Expected counter: 2,000,000")
print(f"Actual counter:   {counter}") # This will likely NOT be 2,000,000

Output (will vary):

Expected counter: 2,000,000
Actual counter:   1498321

The reason is that counter += 1 involves three steps: read, modify, write. If two threads interleave these steps, they can overwrite each other's changes.

Synchronization with Lock

To fix race conditions, you use a Lock. A Lock ensures that only one thread can execute a specific block of code at a time.

import threading
counter = 0
# Create a Lock object
lock = threading.Lock()
def increment_counter():
    global counter
    for _ in range(1_000_000):
        # Acquire the lock before modifying the shared resource
        with lock:
            counter += 1
            # The 'with' statement automatically releases the lock
            # even if an error occurs inside the block
threads = []
for i in range(2):
    thread = threading.Thread(target=increment_counter)
    threads.append(thread)
    thread.start()
for thread in threads:
    thread.join()
print(f"Expected counter: 2,000,000")
print(f"Actual counter:   {counter}") # This will be 2,000,000

When to Use threading vs. multiprocessing

Feature threading multiprocessing
Best For I/O-bound tasks (network calls, file I/O) CPU-bound tasks (math, data processing)
Parallelism No (due to GIL), but provides concurrency Yes (true parallelism on multi-core CPUs)
Memory Threads share memory space. Low overhead. Processes have separate memory. Higher overhead.
Communication Easy (shared variables, but be careful with locks) More complex (requires Queues, Pipes, etc.)
Creation Cost Cheap and fast to create. Slower and more resource-intensive to create.

Summary

  • Use threading when your program needs to wait for external resources (I/O).
  • Use multiprocessing when your program needs to perform heavy computations (CPU).
  • Always be aware of shared resources and use synchronization primitives like Lock to prevent race conditions.
  • The join() method is your best friend for coordinating threads and ensuring the main program waits for them to finish.
分享:
扫描分享到社交APP
上一篇
下一篇