6

I have a function foo that only stops once a condition has been met. While foo is running, I need to ask for user input the whole time (keeps asking for user input). I want them to run separately without interfering with each other.

In my example below, foo keeps printing 'Hello' and getUserInput keeps looking for user input. I want foo to keep printing hello even if i do not enter anything for user input. It will keep asking for input as long as the user does not enter letter 'e'. I have my attempt below:

import threading
from time import sleep

class test:
    def __init__(self):
        self.running = True

    def foo(self):
        while(self.running):
            print 'Hello\n'
            sleep(2)

    def getUserInput(self):
        x = ''
        while(x != 'e'):
            x = raw_input('Enter value: ')
        self.running = False

    def go(self):
        th1 = threading.Thread(target=self.foo)
        th2 = threading.Thread(target=self.getUserInput)
        th1.start()
        th2.start()


t = test()
t.go()

My code prints out the first hello and asks for input but nothing after that. What am I doing wrong? Thanks for your help in advance.

6
  • If you don't enter anything at all, does it still keep printing 'hello'? Commented Sep 11, 2012 at 19:09
  • Which version of python are you running? Commented Sep 11, 2012 at 19:11
  • 2.6.1 on OSX 10.6.8. Also works on 2.5.4 and 2.7.2. Commented Sep 11, 2012 at 19:13
  • Well thats just strange for me then. I have 32 bit python, 2.7.2, on windows 7 (64 bit, some of my modules were acting incompatible with 64 bit hence 32 bit python). Thanks for your input by the way. Commented Sep 11, 2012 at 19:15
  • Works fine here on python 2.7.2 FreeBSD amd64 (64 bit). Commented Sep 11, 2012 at 20:09

1 Answer 1

7

Update: The opener was running his code on Windows in IDLE. Regarding I/O it behaves differently than a shell or the Windows command line. His code works on the Windows command line.

In principle, your code works for me. I am running Python 2.6.5.

Several comments here:

1) In your case it would be fine to only have two threads: the main thread and another one. However, it will also work with three. It's just that your main thread does nothing else than waiting for the other threads to finish.

2) You should to explicitly join() all threads you spawn. You do this in the main thread before terminating it. Keep record of the threads you spawn (e.g. in a list threads) and then join them at the end of your program (e.g. for t in threads: t.join()).

3) You share the variable self.running between threads. It is fine in this case, as one thread only reads it and another one only writes it. In general, you need to be very careful with shared variables and acquire a lock before changing it.

4) You should catch the KeyboardInterrupt exception in the main thread and find a way to communicate to your other threads to terminate :)

5) Use lowercase method names, so instead of getUserInput call it get_user_input. Use uppercase class names and inherit from object: class Test(object):

This is a running example:

import threading
from time import sleep


def main():
    t = Test()
    t.go()
    try:
        join_threads(t.threads)
    except KeyboardInterrupt:
        print "\nKeyboardInterrupt catched."
        print "Terminate main thread."
        print "If only daemonic threads are left, terminate whole program."


class Test(object):
    def __init__(self):
        self.running = True
        self.threads = []

    def foo(self):
        while(self.running):
            print '\nHello\n'
            sleep(2)

    def get_user_input(self):
        while True:
            x = raw_input("Enter 'e' for exit: ")
            if x.lower() == 'e':
               self.running = False
               break

    def go(self):
        t1 = threading.Thread(target=self.foo)
        t2 = threading.Thread(target=self.get_user_input)
        # Make threads daemonic, i.e. terminate them when main thread
        # terminates. From: http://stackoverflow.com/a/3788243/145400
        t1.daemon = True
        t2.daemon = True
        t1.start()
        t2.start()
        self.threads.append(t1)
        self.threads.append(t2)


def join_threads(threads):
    """
    Join threads in interruptable fashion.
    From http://stackoverflow.com/a/9790882/145400
    """
    for t in threads:
        while t.isAlive():
            t.join(5)


if __name__ == "__main__":
    main()

When typing e or E, the program ends after a short delay (as intended by you). When pressing ctrl+c, it immediately terminates. Making a program that uses threading responsive to exceptions is a bit trickier than expected. I have included important references in the source above.

This is how it looks like during runtime:

$ python supertest.py

Hello

Enter 'e' for exit: 
Hello


Hello


Hello

e
$
Sign up to request clarification or add additional context in comments.

11 Comments

Thank a lot. I really appreciate your efforts. I am on Python 3.2.3 now and I am still getting the same behavior even with your code. 'hello' is printed once and then the console just waits for my input without any continuous printing of 'hello' . Any thoughts?
I will edit the answer again and show you how the output looks like for me. Edit: done.
Yes I tried it on 2.7 as well as Python 3 on 2 different hardwares.
IDLE is a very special environment. It is for development, not for production. Please run the code from windows commandline: c:\path\to\python.exe test.py
IDLE might be blocking the output while it's waiting for input. so your thread might be trying to print 'hello' but idle isn't letting it until the input gets resolved. yes, run it in the commandline
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.