Search code examples
pythonmultithreadingdictionaryooppython-multithreading

Is there a way to call a method of a class object thread and have it run in that specific thread?


Context:

I have a threading class, which is called 10 times in a for loop.

For each object of the class, I pass in a username for the user which is determined by its place in the for loop, e.g user0, user1, user2 ect.

I created a method called new_message() in the class, which just prints another message.

In the def run() method of the class, I added the username as key and the new_message() function as the value into a dictionary

My Problem:

I tried to call the new_message() function for one of the users, and that hopelfully the function would run in thread created for that specific user, but instead I think it ran in the main thread, causing my next lines of code to wait.

So my question is:

Is there a way to call a method of a class object thread and have it run in that specific thread?


Do note this is representation of a larger problem I have, but I have made a minimal reproduced example which mimics my code.

Code:

import time

import threading

dict = {

}

class go(threading.Thread):
    def __init__(self, username):
        threading.Thread.__init__(self)
        self.username = username

    def run(self):
        dict[self.username] = self.new_message
        for count in range(10):
            print('Hello: '+self.username+'\n')
            time.sleep(10)

    def new_message(self):
        for count in range(10):
            print('How are you: '+self.username)
            time.sleep(2)


for count in range(10):
    username = ('user' + str(count))
    go(username).start()


what_user = input('What user')
dict[what_user]()

Print('This line shouldn't be waiting to print')


Solution

  • Is there a way to call a method of a class object thread and have it run in that specific thread?

    No this is not possible. You have already start()ed the thread, which makes it run its run() method. You cannot call a method like that to run it in that specific thread -- it'll simply run on the thread that called it as you observed (it ran in your main thread).

    You need the other threads (let's call them worker threads) to be cooperative to externally submitted tasks to make this happen. For instance, you may make the worker threads listen to a work queue after doing the initial work, to pick up tasks (i.e. functions) and run them.

    Here is a quick implementation on your code:

    import time
    
    import threading
    from queue import Queue
    
    dict = {}
    
    
    class Go(threading.Thread):
        def __init__(self, username, work_queue):
            threading.Thread.__init__(self)
            self.username = username
            self.work_queue = work_queue
    
        def run(self):
            self.initialize()
            while True:
                task = self.work_queue.get()
                task()
                self.work_queue.task_done()
    
        def initialize(self):
            dict[self.username] = {"func": self.new_message, "queue": self.work_queue}
            for _ in range(10):
                print("Hello: " + self.username + "\n")
                time.sleep(10)
    
        def new_message(self):
            for _ in range(10):
                print("How are you: " + self.username)
                time.sleep(2)
    
    
    for count in range(10):
        username = "user" + str(count)
        Go(username, Queue()).start()
    
    
    what_user = input("What user")
    selection = dict[what_user]
    queue, method = selection["queue"], selection["func"]
    queue.put(method)
    
    print("This line shouldn't be waiting to print")
    

    You'll observe that the "This line shouldn't be waiting to print" indeed won't wait now. Main thread puts it as a task on the queue for the chosen worker thread. The worker picks that up once it's done with its self.initialize() call.