views:

75

answers:

2

I'm using Python in a webapp (CGI for testing, FastCGI for production) that needs to send an occasional email (when a user registers or something else important happens). Since communicating with an SMTP server takes a long time, I'd like to spawn a thread for the mail function so that the rest of the app can finish up the request without waiting for the email to finish sending.

I tried using thread.start_new(func, (args)), but the Parent return's and exits before the sending is complete, thereby killing the sending process before it does anything useful. Is there anyway to keep the process alive long enough for the child process to finish?

+3  A: 

Take a look at the thread.join() method. Basically it will block your calling thread until the child thread has returned (thus preventing it from exiting before it should).

Update:

To avoid making your main thread unresponsive to new requests you can use a while loop.

while threading.active_count() > 0:
    # ... look for new requests to handle ...
    time.sleep(0.1)  

    # or try joining your threads with a timeout
    #for thread in my_threads:
    #    thread.join(0.1)        

Update 2:

It also looks like thread.start_new(func, args) is obsolete. It was updated to thread.start_new_thread(function, args[, kwargs]) You can also create threads with the higher level threading package (this is the package that allows you to get the active_count() in the previous code block):

import threading
my_thread = threading.Thread(target=func, args=(), kwargs={})
my_thread.daemon = True
my_thread.start()
tgray
That would probably work, but when running as FastCGI, wouldn't it block the script from handling new requests?
crgwbr
I've never used FastCGI, but probably. What you can do is put the join in a while loop and give it a timeout, so it try to join the thread, timeout and look for new requests.
tgray
Setting it as a daemon looks like it'll work. I didn't realize the `start_new` was deprecated.Thanks
crgwbr
A: 

You might want to use threading.enumerate, if you have multiple workers and want to see which one(s) are still running.

Other alternatives include using threading.Event---the main thread sets the event to True and starts the worker thread off. The worker thread unsets the event when if finishes work, and the main check whether the event is set/unset to figure out if it can exit.

Pierce