3
votes

Basically I have a program that starts a new thread, then tries to do stuff in this new thread. However, the new thread seems to get stuck until the main thread reaches the end of the program.

here is a working example of the problem I'm experiencing. it seems that gtk.main() has to be there for this problem to occur. if I use something like input() instead, the problem doesn't appear.

import threading,sys
class MyThread(threading.Thread):
    def run(self):
        import time
        print('before')
        time.sleep(3)
        print('after')

MyThread().start()

from gi.repository import Gtk as gtk

class My_window:
    def __init__(self):
        self.window = gtk.Window()
        self.window.connect("delete_event", gtk.main_quit)
        self.button = gtk.Button("Hello World")
        self.window.add(self.button)
        self.button.show()
        self.window.show()

My_window()
gtk.main()

What should happens: The window appears, the word before appears, and 3 seconds later the word after appears

What really happens: The window appears, the word before appears, then nothing happens. When you close the window, the word after appears. (As if somehow reaching the end of the program makes all the running threads that were frozen before, running again)

Things I've tried:

  • Replaced time.sleep with other functions, like a terminal command, or a busy loop. I get the same results

  • I tried recreating the problem without the window and other extra stuff, but couldn't. so I don't know where this problem resides, so I gave all the background info that I could.

  • I tried using sys.stdout.flush() to make sure that the text was actually showing up in the command line when it should. It was.

Does anyone have any suggestions as to what the problem might be?

I'm using python 3 (and gtk for the window), and would prefer my program to be compatible on all major os's

edit:

I tried putting a print('starting') right before gtk.main(). the output showed before then starting then after. I'm thinking that calling gtk.main freezes all threads, and when gtk.main ends, then the threads resume.

edit 2: never-mind, in my original program, the thread was created while gtk.main is running, but there could still be something in gtk.main that is freezing the threads, that is being called every once in a while.

2
Have you checked that sys.stdout is the real process stdout, and not some weird wrapper added by GTK? Does it work if you write the 'before' and 'after' to a file instead of printing to stdout?Aya
it doesn't seem like it. before and after import gtk I typed in sys.stdout, it showed the same memory address both times.QxQ
@aya, when writing to the file, nothing gets shown to the file until after the window quits. it seems like if the command takes to long to execute, then the command gets stuck. since it takes longer to do f.write('before') then print('before'), I think it gets stuck on f.write('before') instead of at time.sleep(3), so the print doesn't seem to be the problemQxQ
In that above code you are creating an anonymous instance of MyThread. So you can call .start() but you wont be able to call .join() on the thread it creates. Maybe try saving the instance somewhere and calling .join() after you call My_windowqwwqwwq
@qwwqwwq where would I put a .join(), after the gtk.main()? the problem is happening while gtk.main() is running, not after.QxQ

2 Answers

3
votes

I managed to reproduce this, and spent ages trying to work out why it was failing, until I came across this FAQ entry, which suggested putting...

import gobject
gobject.threads_init()

...at the top of the code, which seems to fix the problem.

My guess is that after calling gtk.main(), GTK is holding on to Python's Global Interpreter Lock, which is a bit naughty for default behavior, but at least there's a way to prevent it.

Note that background threads are not able to manipulate GTK objects directly, so you'll have to have them pass messages to the main thread to do that instead.

0
votes

if th is the thread object you created, you should th.setDaemon(1) before starting it, otherwise the main thread won't exit without the thread you started exit first.