1

just following a tutorial and have as far as I know copied it exactly yet mine won't print anything out at all, it should obviously print from each function constantly but I don't seem to be getting anything back, not sure why ?

#!/usr/bin/env python

import thread
import time
import random

def runOften(threadName, sleepTime):
    while 1 < 2:
        time.sleep(sleepTime)
        print "%s" %(threadName)

def runLessOften(threadName, sleepTime):
    while 1 < 2:
        time.sleep(sleepTime)
        print "%s" %(threadName)

def runRandomly(threadName, sleepTime):
    while 1 < 2:
        time.sleep(sleepTime)
        print "%s" %(threadName)

try:
    thread.start_new_thread(runOften, ("Often Runs", 2))
    thread.start_new_thread(runLessOften, ("Less Often Runs!", 2))
    thread.start_new_thread(runRandomly, ("Fast and random", random.random()))

except Exception, e:
    print str(e) 
spergy
  • 49
  • 2
  • 8

3 Answers3

6

Put this at the end of your code:

while True:
    pass

Your program termintes prematurely.

You have nothing in your "MainThread". You spawn 3 threads but do nothing in the so-called "main program". So Python terminates normally.

With the above added to the end I get the following output:

$ python foo.py
Fast and random
Fast and random
Fast and random
Often Runs
 Less Often Runs!
Fast and random
Fast and random
Fast and random
^CTraceback (most recent call last):
  File "foo.py", line 30, in <module>
    while True:
KeyboardInterrupt
James Mills
  • 18,669
  • 3
  • 49
  • 62
4

A better solution is to use the threading module, which provides the Thread class - an object-oriented interface to threads.

This way, we can easily join() the three running threads (wait for them to finish).

Adapting your example:

import threading
import time
import random

keep_running = True

def runOften(threadName, sleepTime):
    while keep_running:
        time.sleep(sleepTime)
        print "%s" %(threadName)

def runLessOften(threadName, sleepTime):
    while keep_running:
        time.sleep(sleepTime)
        print "%s" %(threadName)

def runRandomly(threadName, sleepTime):
    while keep_running:
        time.sleep(sleepTime)
        print "%s" %(threadName)


def main():
    global keep_running

    # Create the threads
    threads = []
    threads.append(threading.Thread(target=runOften, args=("Often Runs", 2)))
    threads.append(threading.Thread(target=runLessOften, args=("Less Often Runs!", 2)))
    threads.append(threading.Thread(target=runRandomly, args=("Fast and random", random.random())))

    # Start the threads
    for t in threads:
        t.start()

    # Wait for all of the threads to finish.
    # Note: KeyboardInterrupt will not be raised inside of a call to join()
    #       with no timeout. So we set an arbitrarily large timeout, which
    #       (for whatever reason) allows KeyboardInterrupt to be raised.
    while threads:
        for t in list(threads):          # Iterate over a copy
            try:
                t.join(1000)             # Arbitrary timeout value
                if not t.isAlive():      # If thread finished (join didn't time-out),
                    threads.remove(t)    # We'll no longer join() on it
            except KeyboardInterrupt:    # If we get a Ctrl+C,
                keep_running = False     # Set global flag, telling threads to stop looping

if __name__ == '__main__':
    main()

Why is this better than (even though it's aesthetically inferior to) while True: pass ? Because that will make the CPU busy-wait for the Ctrl+C, robbing valuable execution time from the other threads. This solution will instead allow the other threads to run, only waking up when Ctrl+C is pressed.

Other questions on handling Ctrl+C with threads:

I should also point out that runRandomly doesn't do what its name implies; instead, it will always sleep the same amount of time every iteration. That amount is randomly decided once when the program starts.

Community
  • 1
  • 1
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • +1 for the use of ``threading.Thread`` (*obviously*). But I'm not sure I like your approach here for the simple reason that it's **hard** to terminate the program without killing it or using ``^\`` :) – James Mills May 21 '14 at 07:26
  • @JamesMills Still trying to figure out how to handle that in an elegant fashion. It's interesting to note that it seems `KeyboardInterrupt` won't be raised from inside of a call to `.join()`. – Jonathon Reinhart May 21 '14 at 07:27
  • I'm not sure there is to be honest. (*Threads suck*). You could do it in an elegant way if you used ``circuits`` :) You'd have to start each component as threads (*a stupid idea really*) but at least the system would terminate nicely. – James Mills May 21 '14 at 07:29
  • @JamesMills Finally, got a solution that works, although "elegant" would probably *not* be the word for it. – Jonathon Reinhart May 21 '14 at 07:35
  • I think it's not a good idea to remove the element from a list when iterating. And join a thread not alive will not block you. – WKPlus May 21 '14 at 07:40
  • @WKPlus Indeed. And if you look closely at my code, you'll see that I am iterating over *a copy* of the list, and removing the item from *the original* list. – Jonathon Reinhart May 21 '14 at 07:46
  • 1
    A similarly contrived example of the OP's program behaviour using circuits: http://codepad.org/cV7HpHpv -- Without any further specifics form the OP the behaviour of this contrived program is all about timers and events :) – James Mills May 21 '14 at 07:48
  • Awesome thanks for this solution! Interesting that the tutorial I was following has it exactly like I did yet his seemed to work? He was using Windows and I am using Linux, but wouldn't think that should change anything. – spergy May 22 '14 at 08:39
0

I'm going to present here a different implementation of the behaviour of the code the OP presented using the circuits application framework because:

  • It's elegant
  • It works
  • It terminates with ^C
  • It doesn't use threads
  • It illustrates the same behaviour.

The behaviour of this program is basically all about timers and events.

Running this produces:

$ python foo.py 
Run Less Often
Run Often
Run Randomly
Run Less Often
Run Less Often
Run Often

Code: http://codepad.org/cV7HpHpv

#!/usr/bin/env python


from random import randint


from circuits import Component, Event, Timer


class App(Component):

    def init(self):
        Timer(5, Event.create("run_often"), persist=True).register(self)
        Timer(3, Event.create("run_less_often"), persist=True).register(self)
        Timer(randint(1, 10), Event.create("run_randomly")).register(self)

    def run_often(self):
        print "Run Often"

    def run_less_often(self):
        print "Run Less Often"

    def run_randomly(self):
        print "Run Randomly"

        Timer(randint(1, 10), Event.create("run_randomly")).register(self)


App().run()
James Mills
  • 18,669
  • 3
  • 49
  • 62
  • Interesting, does this run the same as using threading? It didn't run for me :( – spergy May 22 '14 at 08:52
  • You'll want to firstly: ``pip install circuits==3.0.0.dev`` and no it doesn't use threads. it's completely event-driven and asynchronous. – James Mills May 22 '14 at 13:02