Tomy137
Tomy137

Reputation: 111

Python - Callback from method to another class

Great fellows !

I read lot of topics about this subject but I can't make it works.

This is my exemple script :

import time, threading, collections


class Factory () :

    def __init__(self):
        self.to_do_list = collections.deque()

    def run(self):
        while self.to_do_list :
            new_task = self.to_do_list.popleft()
            new_task.start(self.done_callback)

    def add_worker(self, task):
        self.to_do_list.append(task)

    def done_callback(self):
            print("OK - DONE")

class Worker (threading.Thread):

    def __init__(self, _name):
        threading.Thread.__init__(self)
        self.name = _name

    def run(self, _callback):
        self.callback = _callback
        for i in range (0,5):
            print(self.name+" -- "+str(i))
            time.sleep(0.5)     
        self.callback()

worker_1 = Worker("Anakin")
worker_2 = Worker("Obi-Wan")
worker_3 = Worker("Yoda")

factory = Factory()

factory.add_worker(worker_1)
factory.add_worker(worker_2)
factory.add_worker(worker_3)

factory.run()

When I run my Workers with run(self.done_callback) it's working well but I loose the multi-threading effect.

And if I run it with the start(self.done_callback) I'm facing this error message : TypeError: start() takes 1 positional argument but 2 were given

Can you explain to me what i'm doing wrong ?

Thanks in advance for your time !

Upvotes: 1

Views: 555

Answers (2)

Tomy137
Tomy137

Reputation: 111

V2 :

import time, threading, collections

class Factory () :

    def __init__(self):
        self.to_do_list = collections.deque()

    def run(self):
        while self.to_do_list :
            new_task = self.to_do_list.popleft()
            new_task.callback = self.done_callback
            new_task.start()

    def add_worker(self, task):
        self.to_do_list.append(task)

    def done_callback(self):
            print("OK - DONE")

class Worker (threading.Thread):

    def __init__(self, _name):
        threading.Thread.__init__(self)
        self.name = _name

    def run(self):

        for i in range (0,5):
            print(self.name+" -- "+str(i))
            time.sleep(0.5)     
        self.callback()

factory = Factory()

worker_1 = Worker("Tomtom")
worker_2 = Worker("Nana")
worker_3 = Worker("Pikachu")

factory.add_worker(worker_1)
factory.add_worker(worker_2)
factory.add_worker(worker_3)

factory.run()

It works !

Upvotes: 0

jalm
jalm

Reputation: 56

First, when you run your workers with new_task.start(self.done_callback) fails because you are calling threading.Thread.start() which only receives self object parameter.

  • Note that when you call new_task.start() you are passing new_task as first argument implicitly, what's equivalent to: threadhing.Thread.start(new_task)

Also, from docs:

Once a thread object is created, its activity must be started by calling the thread’s start() method. This invokes the run() method in a separate thread of control.

So you have no control on how threading.Thread.run() is invoked, and you are overriding such method with different signature, by adding the _callback argument.

This doesn't make sense for me, since you are subclassing threading.Thread and overriding run() without providing its actual functionality, or splitting it in two "pieces".

In case you really need to do in that way, I would suggest not to subclass Thread and replace your Worker with something like this:

class Worker:

def __init__(self, _name):
    self.thread = None
    self.name = _name

def run(self, _callback):
    self.thread = threading.Thread(target=self._p_run, args=(_callback,))
    self.thread.start()

def _p_run(self, _callback):
    for i in range(0, 5):
        print(self.name+" -- "+str(i))
        time.sleep(0.5)
    _callback()

You still can access threading.Thread interface through Worker.thread in case you need.

Upvotes: 1

Related Questions