【Python】Creating Custom Thread Classes by Inheriting the Thread Class

目次

Overview

This article explains how to implement multithreading by inheriting (subclassing) the Thread class in the Python threading module. Compared to simply passing a function to the target argument, this approach is effective when you want the thread to hold its own data (state) or when you want to encapsulate complex logic within a class to improve code readability.

Specifications (Input/Output)

  • Input: Thread-specific names or parameters (during initialization).
  • Output: Concurrent execution logs from each thread.
  • Requirement: Inherit from threading.Thread and override the run() method.

Basic Usage

Create a class that inherits from threading.Thread and write the processing logic inside the run() method.

import threading

class MyThread(threading.Thread):
    def run(self):
        # This method is called in a separate thread when start() is executed
        print("Thread processing is running")

t = MyThread()
t.start() # Do not call run() directly; always call start()
t.join()

Full Code

Below is a complete implementation example where we define separate classes for a “Number Printing Thread” and a “Character Printing Thread.” We pass names during __init__ and store them as instance variables.

import threading
import time

class NumberPrinterThread(threading.Thread):
    """Thread class that prints numbers in a count-up fashion"""
    
    def __init__(self, thread_name, loops=5):
        # Always initialize the parent class (Thread) first
        super().__init__()
        self.thread_name = thread_name
        self.loops = loops

    def run(self):
        """Main process executed by the thread"""
        print(f"[{self.thread_name}] Started")
        for i in range(self.loops):
            print(f"{self.thread_name}: {i}")
            time.sleep(1)
        print(f"[{self.thread_name}] Completed")

class LetterPrinterThread(threading.Thread):
    """Thread class that prints characters from a list sequentially"""

    def __init__(self, thread_name, chars=None):
        super().__init__()
        self.thread_name = thread_name
        # Set default arguments
        self.chars = chars if chars else ["A", "B", "C", "D", "E"]

    def run(self):
        print(f"[{self.thread_name}] Started")
        for letter in self.chars:
            print(f"{self.thread_name}: {letter}")
            time.sleep(1.5)
        print(f"[{self.thread_name}] Completed")

if __name__ == "__main__":
    # Create instances of each class
    thread1 = NumberPrinterThread("Num-Thread")
    thread2 = LetterPrinterThread("Char-Thread")

    # Start the threads
    # Calling start() executes the run() method internally in a separate thread
    thread1.start()
    thread2.start()

    # Wait for completion
    thread1.join()
    thread2.join()

    print("All processes have finished")

Customization Points

Mandatory Rules for Subclassing

Overriding the run() method

Describe the process to be executed when the thread starts here. Only the code written in this method runs in the separate thread.

Calling super().init() in init()

If you define a constructor (initialization method), you must call super().__init__() at the very beginning. If you forget this, thread functions (like start() or the name attribute) will not initialize correctly, leading to errors.

Benefits

  • Data Persistence: It becomes easy to manage thread-specific data as instance variables, such as self.data.
  • Structural Clarity: You can divide complex processing into methods within the class, making large-scale thread management easier to understand.

Important Notes

Do not call run() directly

If you call the .run() method directly on an instance, a new thread will not be created. Instead, the code will run synchronously on the main thread. Always call the .start() method.

Thread Safety

Using a class does not automatically make access to shared resources safe. If multiple threads modify the same object attributes, you must use exclusive control, such as threading.Lock.

Advanced Usage

This is an advanced example where the thread has a stop flag so it can be safely terminated from the outside. Classifying the thread allows for clean management of such “control states.”

import threading
import time

class StoppableThread(threading.Thread):
    def __init__(self):
        super().__init__()
        self._stop_event = threading.Event()

    def stop(self):
        """Method to stop the thread from the outside"""
        self._stop_event.set()

    def run(self):
        print("Starting process...")
        while not self._stop_event.is_set():
            print("Running...")
            time.sleep(1)
            # Write your main logic here
        print("Stop instruction received. Terminating.")

# Usage Example
if __name__ == "__main__":
    t = StoppableThread()
    t.start()
    time.sleep(3)
    t.stop() # Stop from outside
    t.join()

Summary

The style of inheriting threading.Thread is an object-oriented approach suitable for when you want the thread to have its own states (variables) and methods (operations). The Pythonic design is to choose based on the task: use a function (target=func) for simple tasks and class inheritance for complex ones.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次