Reputation: 1818
I would like to stop a Python thread when the main program stops. It is for a class that connects to a server. The connection is maintained by the background thread, the foreground thread answers to callbacks. The following is a minimal example.
#!/usr/bin/python
import time, threading
class test():
running = False
def __init__(self):
print "init"
self.running = True
self.thread = threading.Thread(target = self.startThread)
self.thread.start()
def __del__(self):
running = False
print "del"
def startThread(self):
print "thread start"
while self.running:
time.sleep(1)
print "thread running"
a = test()
When the program ends, I would naively expect __del__() to be called so that the background thread can be informed to stop, but i is not called untill after the background thread stops. Explicitly calling some function is not an option since the class is used by other people, whom I do not want to force to use some extra lines of code.
Upvotes: 5
Views: 6809
Reputation: 597
I was facing the same problem and finally found the answer to this question in the answer to another question.
Use the daemon = True
flag. Documentation for 3.x and 2.x
So your code would be this:
#!/usr/bin/python
import time, threading
class test():
def __init__(self):
print "init"
self.thread = threading.Thread(target = self.startThread)
self.thread.daemon = True
self.thread.start()
def startThread(self):
print "thread start"
while True:
time.sleep(1)
print("thread running")
a = test()
Remark: this is propably not cleanly ending your thread, but it stops it when you exit your program, I guess this is what you were looking for.
Upvotes: 1
Reputation: 435
You forgot to change the "running" variable of your current test() instance, in your del function. It should be
def __del__(self):
self.running = False
print "del"
That should do the trick.
Upvotes: 0
Reputation: 15756
As per gnibbler's comment, it may be better to use a context manager for explicit resource deallocation. Opinions appear to vary on whether __del__
should be used for resource deallocation. A couple of good posts on the subject are here and here.
If you're used to a language like C++ where RAII is used, it's sometimes a bit difficult to get accustomed to the idea that destructors in Python may not be called when you expect, if at all, usually because of how reference counting and garbage collection works.
So, the usual approach in Python is to use a context manager which can be used to provide explicit deallocation of resource.
A simple threading example might look like this (untested):
#!/usr/bin/python
import time, threading
class test():
def __init__(self):
print "init"
self.stop_event = threading.Event()
self.thread = threading.Thread(target = self.startThread)
self.thread.start()
def startThread(self):
print "thread start"
while not self.stop_event.isSet():
time.sleep(1)
print "thread running"
def close(self):
# Request thread to stop.
self.stop_event.set()
# Wait for thread to exit.
self.thread.join()
def __enter__(self):
# Nothing to do, thread already started. Could start
# thread here to enforce use of context manager.
def __exit__(self, exc_type, exc_value, traceback):
self.close()
The test()
class is then used like this in a context manager:
with test():
# Thread will be active here.
pass
# Thread will have been stopped and joined.
or, use the Python contextlib.closing helper function which will ensure that close
is called on exit.
import contextlib
with contextlib.closing(test()):
# Thread will be active here.
# But not here
Upvotes: 1
Reputation: 12900
__del__
is not called as long as there are references to self
, and you have one such reference in the background thread itself: in the self
argument of def startThread(self):
.
You need to move the function that runs the background thread outside of the class. And rather than a __del__
I would recommend for example using a weakref, as follows. This code should work without the __del__()
method and without using a self.running
attribute.
self.thread = threading.Thread(target=run_me, args=(weakref.ref(self),))
...
def run_me(weak_self):
"Global function"
while True:
self = weak_self()
if self is None: break # no more reference to self
...
del self # forget this reference for now
time.sleep(1)
Upvotes: 3