Sinan Cetinkaya
Sinan Cetinkaya

Reputation: 457

Passing a variable through threaded classes

In the test code below,

class1.stop_callback() sets class1.stop = True

therefore class2.stop = True

therefore class3.stop should be True but it isn't.

class1.stop_callback() should stop the program but it doesn't do that. What am I doing wrong?

You can test the code on repl.it https://repl.it/@bahtsiz_bedevi/classtest

import threading
import time


class Class1(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop = False

    def stop_callback(self):
        self.stop = True

    def run(self):
        class2 = Class2()
        class2.stop = self.stop
        class2.start()
        while True:
            time.sleep(1)
            print("{} stop status: {}".format(self.__class__, "True" if self.stop else "False"))
            if self.stop:
                break


class Class2(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop = False

    def run(self):
        class3 = Class3()
        class3.stop = self.stop
        while True:
            time.sleep(1)
            print("{} stop status: {}".format(self.__class__, "True" if self.stop else "False"))
            if self.stop:
                break
            class3.foo()


class Class3:
    def __init__(self):
        self.stop = False

    def foo(self):
        while True:
            time.sleep(1)
            print("{} stop status: {}".format(self.__class__, "True" if self.stop else "False"))
            if self.stop:
                break


class1 = Class1()
class1.start()
for i in range(10):
    time.sleep(1)
class1.stop_callback()

Upvotes: 1

Views: 78

Answers (2)

speedstyle
speedstyle

Reputation: 141

Since Class3 is not a Thread-like class (despite not running in the main thread) you cannot change the value of class3.stop until class3.foo() returns. Since class3.foo() doesn't return until the value of class3.stop changes, there is no way to stop the process and it runs forever.

I would suggest basing Class3 on Thread so that you can call methods on it while it is running. If this is too much overhead, or you will be running it more than once per instance of class2, you could always just define foo and then run it within the Class2.run method.


EDIT: I was going to mention Florian's point, but since - as in his proposed solution - mutable objects do carry across during assignments, I wasn't sure if you had already thought this part through.

Below is revised code; note

  • the use of threading.Lock to prevent those weird print statements that were happening on the same line
  • the use of while not self.stop rather than if statements with breaks
  • the use of threading in Class3

import threading
import time

printLock = threading.Lock()
p = print

def print(*a, **b):
    with printLock:
        p(*a, **b)


class Class1(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop = False

    def stopMe(self):
        self.stop = True

    def run(self):
        class2 = Class2()
        class2.start()
        while not self.stop:
            time.sleep(1)
            print("{} stop status:{:6}".format(self.__class__, str(self.stop)))
        class2.stopMe()


class Class2(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop = False

    def stopMe(self):
        self.stop = True

    def run(self):
        class3 = Class3()
        class3.start()
        while not self.stop:
            time.sleep(1)
            print("{} stop status:{:6}".format(self.__class__, str(self.stop)))
        class3.stopMe()


class Class3(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stop = False

    def stopMe(self):
        self.stop = True

    def run(self):
        while not self.stop:
            time.sleep(1)
            print("{} stop status:{:6}".format(self.__class__, str(self.stop)))


class1 = Class1()
class1.start()
time.sleep(10)
class1.stopMe()

Upvotes: 1

Florian Rhiem
Florian Rhiem

Reputation: 1808

In Python, variables are names for objects. By assigning False to class1.stop and class1.stop to class2.stop, you are assigning False to class2.stop, nothing more.

What you seem to want is a reference to class1.stop instead, however this is not how assignment works in Python. One way to get around this would be to use a list. If you keep the list the same and only change the value at the first index, you can achieve what you want:

stop1 = [False]
stop2 = stop1
assert stop2[0] == False
stop1[0] = True
assert stop2[0] == True

Upvotes: 1

Related Questions