Python: Function from different class not running with thread - python

Im trying to implement a gui for a script I have written.
The script itself runs perfectly fine but as soon as I try to run it using a thread, it will just freeze the whole process without any feedback.
The thread seems to not execute the function.
import Foo
from multiprocessing.pool import ThreadPool
class GUI():
def __init__(self, master):
self.file = "path/file"
self.key = "path/key"
# init GUI
...
def updateDev(self):
fib = Foo.foo()
pool = ThreadPool(processes=1)
async_res = pool.apply_async(fib.update, args=(self.file, self.key))
async_res.wait()
# the code freezes here
res = async_res.get()
...
Is there anything im missing?
fib.update(self.file, self.key) runs like this without any threads perfectly.
EDIT:
I solved it myself yesterday by adding a function that starts the thread.
import Foo
from threading import Thread
class GUI():
def __init__(self, master):
self.file = "path/file"
self.key = "path/key"
# init GUI
...
def startDev(self):
t = Thread(target = self.updateDev)
t.start()
def updateDev(self):
fib = Foo.foo()
fib.update(self.file, self.key)
...
Thanks for the help everyone!

Sorry but I don't understand what is the pourpose of the code. If I correctly understand, the code will never reach the async_res.wait() satement because it is recoursive. This row async_res = pool.apply_async(fib.update, args=(self.file, self.key)) will probably run again the "update" function

Related

Thread hangs when trying to write to tkinter IntVar when user closes the window

I'm using threading to run a long task, but I ran into an issue. The thread just hung while trying to set an IntVar after I clicked the close button. It doesn't even error. I don't want to use a daemon thread because the function is a critical part of the program, which might have consequences if it stops midway through (it deals with a bunch of files).
Here's an oversimplified version of my program, meant to demonstrate my issue.
import tkinter as tk
import tkinter.ttk as ttk
import threading
class Window(tk.Tk):
def __init__(this, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
this.threads = []
this.var = tk.IntVar(value=0)
this.label = ttk.Label(textvariable=this.var)
this.button = ttk.Button(text='Start counter', command=this.startCounter)
this.label.pack()
this.button.pack()
this.stop = False
this.protocol("WM_DELETE_WINDOW", this.close)
def startCounter(this):
thread = threading.Thread(target=this.counter)
this.threads.append(thread)
thread.start()
def counter(this):
while True:
if this.stop:
print(f'self.stop = ')
break
this.var.set(this.var.get() + 1)
def close(this):
print('Stopping threads')
this.stop = True
this.waitThreads()
print('Stopped threads')
this.destroy()
def waitThreads(this):
for thread in this.threads:
thread.join()
Window().mainloop()
My program is using an InVar for a progress bar, not a counter, this was just the best way I could demonstrate the issue.
I tried a bunch of different methods to stop all threads, but none of them worked (that was before I knew what the issue was). For some reason in my actual program, if I log the var and the value of the var before the stop check, it's able to stop. I could not reproduce that with my test script.
I'm expecting the set var line to move on, or error instead of just hang.
Why is it hanging, and how can I fix this? I want to be able to safely stop the thread(s), and I don't want to use a daemon.
you have a race condition, a deadlock, and an undefined behavior in your application ... that's how simple it is to mess up a small code snippet when multithreading.
the tk interpreter isn't threadsafe, and shouldn't be called from different threads, notably the event_generate function is threadsafe and should be used for instructing GUI changes, incrementing the variable from another thread is likely going to crash the interpreter, it's also a race condition, and the results will be wrong, increments should only happen in the main thread, by generating an event from the other thread.
lastly you need to make your threads drop the GIL momentarily, this can be done by a small sleep time.sleep(0.0000001).
import tkinter as tk
import tkinter.ttk as ttk
import threading
import time
class Window(tk.Tk):
def __init__(this, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
this.threads = []
this.var = tk.IntVar(value=0)
this.label = ttk.Label(textvariable=this.var)
this.button = ttk.Button(text='Start counter', command=this.startCounter)
this.bind("<<increment_counter>>",this.increment_var)
this.label.pack()
this.button.pack()
this.stop = False
this.protocol("WM_DELETE_WINDOW", this.close)
def startCounter(this):
thread = threading.Thread(target=this.counter)
this.threads.append(thread)
thread.start()
def increment_var(this, event):
this.var.set(this.var.get() + 1)
def counter(this):
while True:
time.sleep(0.0000001) # drop the GIL momentarily
if this.stop:
print(f'self.stop = ')
break
this.event_generate("<<increment_counter>>") # all increments happen in main thread
def close(this):
print('Stopping threads')
this.stop = True
this.waitThreads()
print('Stopped threads')
this.destroy()
def waitThreads(this):
for thread in this.threads:
thread.join()
Window().mainloop()
note that the first argument of a method is by convention called self in python, calling it this will confuse a lot of linters, parsers, other coders, autocorrect IDEs and documentation generators. please don't do that to everyone and use self instead of this.
I have a few suggestions to make your code work safer.
Use a costume defined event. This will place an event in the event queue of tkinters event-loop.
Have a thread limit, too many threads might mess things up an become unreachable.
Use a threading primitive to signal the termination of your threads, like threading.Event()
Instead of joining the threads within the event-loop of tkinter, make sure they are done after your app has been terminated. This will lead to a better user-experience.
In addition I would propose:
Using self instead of this because of convention
Using a set() instead of a list()
import tkinter as tk
import tkinter.ttk as ttk
import threading
import time
class Window(tk.Tk):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.threads = set()
self.var = tk.IntVar(value=0)
self.label = ttk.Label(self, textvariable=self.var)
self.button = ttk.Button(
self, text='Start counter', command=self.startCounter)
self.label.pack()
self.button.pack()
self.stop = threading.Event()
self.event_generate('<<UpdateLabel>>')
self.bind('<<UpdateLabel>>', self.update_counter)
self.protocol("WM_DELETE_WINDOW", self.close)
def update_counter(self, event):
self.var.set(self.var.get() + 1)
def startCounter(self):
if threading.active_count()+1 <= THREAD_LIMIT:
t = threading.Thread(target=self.counter)
self.threads.add(t)
t.start()
else:
print('too many threads might messing things up')
def counter(self):
current = threading.current_thread()
while not self.stop.is_set():
time.sleep(1) #simulate task
if not self.stop.is_set():
#if application still exists
self.event_generate('<<UpdateLabel>>')
else:
self.threads.discard(current)
break
self.threads.discard(current)
def close(self):
print('Stopping threads')
self.stop.set()
print('Stop event is set')
self.destroy()
if __name__ == '__main__':
COUNT = threading.active_count()
THREAD_LIMIT = COUNT + 7
window = Window()
window.mainloop()
print('mainloop ended')
while threading.active_count() > COUNT: #simulate join
time.sleep(0.1)
print(threading.active_count())
print('all threads ended, mainthread ends now')

What happens if the function of a python thread is completed?

I am using python thread while I found no method to stop it.
Here is how I use the thread:
class MyThread(Thread):
def __init__(self, func, args=()):
Thread.__init__(self)
self.__return_value = None
self.func = func
self.args = args
self.func_name = func.__name__
def run(self):
self.__return_value = self.func(*self.args)
Considering there is no explicit way to stop it, I try to ignore it when it finishes the function to execute.
Will a zombie thread left if I do nothing when it finishes?
No - the thread pack up after itself and shuts down cleanly.
It is how things in Python try to work, after all.
import threading
import time
def worker():
time.sleep(1)
def main():
print (threading.active_count())
t = threading.Thread(target=worker)
t.start()
print(threading.active_count())
time.sleep(2)
print(threading.active_count())
return t
main()
t = main()
t.is_alive()
Running this snippet in ipython (an interactive prompt which uses some threads for its own purposes) will print
4
5
4
False

Daemon thread stopping automatically?

Here is a bit of the scenario:
File A:
from fileb import StatusClass
class MainClass (tk.Tk):
#init
...
self.anotherclass = StatusClass(parent, controller)
#DOSOMESTUFF
self._startprgram()
File B:
class StatusClass (tk.Frame):
#init
...
self.anotherclass = StatusProcess
class StatusProcess (threading.Thread):
#init method
self.setDaemon(True)
def run():
while True:
#do stuff
What happens is that, depending on the run time in #DOSOMESTUFF in file A, the thread that is created stops.
If I have a few instructions it holds, but if I even add a time.sleep for a few seconds the thread also stops.
Any suggestion?

How to end with a thread from the main thread?

I'm looking for this question online but I can not find any way to do it directly I'm trying the following
class Test(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
for i in range(3):
time.sleep(1)
print(i)
def main():
test = Test()
test.start()
del test
time.sleep(5)
print('end')
main()
the only way to stop the thread is from the run method when the code ends but I can not find any way to end the thread.
You can't. All you can do is ask it nicely (by implementing some sort of inter thread communication like a threading.Queue object, then making your thread check it for instructions) and hope for the best.
You can use this simple approach to stop/kill/end a child thread from the parent thread using some variable that is being checked in child thread periodically:
from threading import Thread
from time import time, sleep
class Test:
some_var = True
def __init__(self):
self.t = Thread(target=self.worker)
#self.t.setDaemon(True)
self.t.start()
def worker(self):
while self.some_var is True:
print("%s > I'm running" % str(time()))
test = Test()
sleep(2)
test.some_var = False
print("End!")
Let me know if I didn't understand your question, but I think I've answered your question "How to end with a thread from the main thread?".

Non-blocking class in python (detached Thread)

I'm trying to create a kind of non-blocking class in python, but I'm not sure how.
I'd like a class to be a thread itself, detached from the main thread so other threads can interact with it.
In a little example:
#!/usr/bin/python2.4
import threading
import time
class Sample(threading.Thread):
def __init__(self):
super(Sample, self).__init__()
self.status = 1
self.stop = False
def run(self):
while not(self.stop):
pass
def getStatus(self):
return self.status
def setStatus(self, status):
self.status = status
def test(self):
while self.status != 0:
time.sleep(2)
#main
sample = Sample()
sample.start()
sample.test()
sample.setStatus(0)
sample.stop()
What I'd like is having the "sample" instance running as a separate thread (detached from the main one) so, in the example, when the main thread reaches sample.test(), sample (and only "sample") would go to sleep for 2 seconds. In the meanwhile, the main thread would continue its execution and set sample's status to 0. When after the 2 seconds "sample" wakes up it would see the status=0 and exit the while loop.
The problem is that if I do this, the line sample.setStatus(0) is never reached (creating an infinite loop). I have named the threads, and it turns out that by doing this, test() is run by the main thread.
I guess I don't get the threading in python that well...
Thank you in advance
The object's run() method is what executes in a separate thread. When you call sample.test(), that executes in the main thread, so you get your infinite loop.
Perhaps something like this?
import threading
import time
class Sample(threading.Thread):
def __init__(self):
super(Sample, self).__init__()
self.stop = False
def run(self):
while not(self.stop):
print('hi')
time.sleep(.1)
def test(self):
print('testing...')
time.sleep(2)
#main
sample = Sample()
sample.start() # Initiates second thread which calls sample.run()
sample.test() # Main thread calls sample.test
sample.stop=True # Main thread sets sample.stop
sample.join() # Main thread waits for second thread to finish

Categories