0

I am trying to combine the answers I got from two different python questions.

Here is the the first question and answer. Basically I just wanted to spawn two threads, one to powerDown() and the other to powerUp(), where powerUp() pends on powerDown()

How to spawn a thread inside another thread in the same object in python?

import threading

class Server(threading.Thread):
    # some code
    def run(self):
        self.reboot()

    # This is the top level function called by other objects
    def reboot(self):
        # perhaps add a lock
        if not hasattr(self, "_down"):
            self._down = threading.Thread(target=self.__powerDown)
            self._down.start()
            up = threading.Thread(target=self.__powerUp)
            up.start()

    def __powerDown(self):
        # do something

    def __powerUp(self):
        if not hasattr(self, "_down"):
            return
        self._down.join()
        # do something
        del self._down

Here is the the second question and answer. Basically I wanted to start a thread, and then call a function of the object.

How to call a function on a running Python thread

import queue
import threading

class SomeClass(threading.Thread):
    def __init__(self, q, loop_time = 1.0/60):
        self.q = q
        self.timeout = loop_time
        super(SomeClass, self).__init__()

    def onThread(self, function, *args, **kwargs):
        self.q.put((function, args, kwargs))

    def run(self):
        while True:
            try:
                function, args, kwargs = self.q.get(timeout=self.timeout)
                function(*args, **kwargs)
            except queue.Empty:
                self.idle()

    def idle(self):
        # put the code you would have put in the `run` loop here 

    def doSomething(self):
        pass

    def doSomethingElse(self):
        pass

Here is combined idea code. Basically I wanted to spawn a thread, then then queue up a functions to execute, which in this case is reboot(). reboot() in turns creates two threads, the powerDown() and powerUp() threads, where powerDown() pends on powerUp()

import threading
import Queue

class Server(threading.Thread):
    def __init__(self, q, loop_time = 1.0/60):
        self.q = q
        self.timeout = loop_time
        super(Server, self).__init__()

    def run(self):
        while True:
            try:
                function, args, kwargs = self.q.get(timeout=self.timeout)
                function(*args, **kwargs)
            except queue.Empty:
                self.idle()

    def idle(self):
        # put the code you would have put in the `run` loop here 

    # This is the top level function called by other objects
    def reboot(self):
        self.__onthread(self.__reboot)

    def __reboot(self):
        if not hasattr(self, "_down"):
            self._down = threading.Thread(target=self.__powerDown)
            self._down.start()
            up = threading.Thread(target=self.__powerUp)
            up.start()

    def __onThread(self, function, *args, **kwargs):
        self.q.put((function, args, kwargs))

    def __powerDown(self):
        # do something

    def __powerUp(self):
        if not hasattr(self, "_down"):
            return
        self._down.join()
        # do something
        del self._down

All work, except when I create two Server subclasses.

class ServerA(Server):
    pass

class ServerB(Server):
    pass

Here is the code that instatiats both subclasses, and call the start() and reboot functions

serverA = ServerA(None)
serverB = ServerB(None)
serverA.start()
serverB.start()
serverA.reboot()
serverB.reboot()

I expect serverA.reboot() and serverB.reboot() to happen concurrently, which is what I want, but they DO NOT! serverB.reboot() gets executed after serverA.reboot() is done. That is, if I put print statements, I get

serverA started
serverB started
serverA.reboot() called
serverA.__powerDown called
serverA.__powerUp called
serverB.reboot() called
serverB.__powerDown called
serverB.__powerUp called

I know for a fact that it takes longer for ServerA to reboot, so I expect something like this

serverA started
serverB started
serverA.reboot() called
serverB.reboot() called
serverA.__powerDown called
serverB.__powerDown called
serverB.__powerUp called
serverA.__powerUp called

I hope that makes sense. If it does, why aren't my reboot() functions happening simultaneously?

Community
  • 1
  • 1
Chris F
  • 14,337
  • 30
  • 94
  • 192
  • If I just use the sample code from the first post, I can non-sequentially execute code; that is, I get my expected non-sequential powerDown/powerUp's from the two server objects. – Chris F Sep 26 '13 at 21:13

1 Answers1

1

Why are you sending None while you are expecting a queue object in the first place ? This causes an exception which complains that None type object doesn't have a get method. Besides that the exception you want to be handled in the run method is Queue.Empty and not queue.Empty.

Here is the revised code and its output on my machine:

import threading
import Queue

class Server(threading.Thread):
    def __init__(self, title, q, loop_time = 1.0/60):
        self.title = title
        self.q = q
        self.timeout = loop_time
        super(Server, self).__init__()

    def run(self):
        print "%s started" % self.title
        while True:
            try:
                function, args, kwargs = self.q.get(timeout=self.timeout)
                function(*args, **kwargs)

            except Queue.Empty:
                # print "empty"
                self.idle()

    def idle(self):
        pass
        # put the code you would have put in the `run` loop here 

    # This is the top level function called by other objects
    def reboot(self):
        self.__onThread(self.__reboot)

    def __reboot(self):
        if not hasattr(self, "_down"):
            self._down = threading.Thread(target=self.__powerDown)
            self._down.start()
            up = threading.Thread(target=self.__powerUp)
            up.start()

    def __onThread(self, function, *args, **kwargs):
        self.q.put((function, args, kwargs))

    def __powerDown(self):
        # do something
        print "%s power down" % self.title
        pass

    def __powerUp(self):
        print "%s power up" % self.title

        if not hasattr(self, "_down"):
            return

        self._down.join()
        # do something
        del self._down

class ServerA(Server):
    pass

class ServerB(Server):
    pass

def main():
    serverA = ServerA("A", Queue.Queue())
    serverB = ServerB("B", Queue.Queue())
    serverA.start()
    serverB.start()
    serverA.reboot()
    serverB.reboot()

if __name__ == '__main__':
    main()

Output:

A started
B started

B power down
A power down
B power up
A power up