How to run multiple instances of an object with two threads inside each object?

45 views Asked by At

I'm trying to wrap two threaded functions inside an object and run a couple of instances of the object. So far I could only run the objects if I pull the threading outside the class.

import threading

class MyObject:
    
    def __init__(self, item_id):
        self.item_id = item_id

    def func_one(self):
        # do stuff

    def func_two(self):
        # do another thing

# initialize objects
obj1 = MyObject("item-01")
obj2 = MyObject("item-02")

# create threads for each object
thread1_obj1 = threading.Thread(target=obj1.func_one)
thread2_obj1 = threading.Thread(target=obj1.func_two)
thread1_obj2 = threading.Thread(target=obj2.func_one)
thread2_obj2 = threading.Thread(target=obj2.func_two)

# start and join threads
thread1_obj1.start()
thread2_obj1.start()
thread1_obj2.start()
thread2_obj2.start()

thread1_obj1.join()
thread2_obj1.join()
thread1_obj2.join()
thread2_obj2.join()

Is there a way to encapsulate the threads inside the class instead of pulling them out to run the the objects?

3

There are 3 answers

1
Barmar On BEST ANSWER

Define a method in the class that runs both functions, each in its own thread. Then call that method from the main thread.

import threading

class MyObject:
    
    def __init__(self, item_id):
        self.item_id = item_id

    def func_one(self):
        # do stuff

    def func_two(self):
        # do another thing

    def run_threads(self):
        self.thread1 = threading.Thread(target=self.func_one)
        self.thread2 = threading.Thread(target=self.func_two)
        self.thread1.start()
        self.thread2.start()

    def join_threads(self):
        self.thread1.join()
        self.thread2.join()

obj1 = MyObject("item-01")
obj2 = MyObject("item-02")

obj1.run_threads()
obj2.run_threads()

obj1.join_threads()
obj2.join_threads()
0
R.Orteza On

I figured it out. Here's the way to encapsulate the threads inside the class:

import threading

class MyObject:
    
    def __init__(self, item_id):
        self.item_id = item_id
        self.t1 = threading.Thread(target=self.func_one)
        self.t2 = threading.Thread(target=self.func_two)
    
    def thread_start(self):
        self.t1.start()
        self.t2.start()
    
    def thread_join(self):
        self.t1.join()
        self.t2.join()

    def func_one(self):
        # do stuff

    def func_two(self):
        # do another thing

# initialize objects
obj1 = MyObject("item-01")
obj2 = MyObject("item-02")

# run and join objects' threads
obj1.thread_start()
obj2.thread_start()
obj1.thread_join()
obj2.thread_join()
0
Mark Tolonen On

To completely encapsulate, __init__ can create and start each thread and a join method can stop them. Using __enter__ and __exit__ the class instances can be used with with to automatically stop the threads when the objects go out-of-scope:

import threading
import time

class MyObject:

    def __init__(self, item_id):
        self.item_id = item_id
        self.thread1 = threading.Thread(target=self.func_one, daemon=True)
        self.thread2 = threading.Thread(target=self.func_two, daemon=True)
        self.running = True
        self.thread1.start()
        self.thread2.start()

    def func_one(self):
        while self.running:
            print(f'{self.item_id} func_one')
            time.sleep(1)

    def func_two(self):
        while self.running:
            print(f'{self.item_id} func_two')
            time.sleep(1)

    def join(self):
        self.running = False
        self.thread1.join()
        self.thread2.join()

    def __enter__(self):
        return self

    def __exit__(self, exec_type, exc_val, exc_tb):
        self.join()

with MyObject("item-01") as obj1, MyObject('item-02') as obj2:
    time.sleep(3)

Output:

item-01 func_one
item-01 func_two
item-02 func_one
item-02 func_two
item-01 func_one
item-02 func_two
item-02 func_one
item-01 func_two
item-01 func_one
item-01 func_two
item-02 func_two
item-02 func_one
item-01 func_one
item-01 func_two