1

I am having the Python Multi-threaded program as below. If I press ctrl+c within 5 seconds (approx), It is going inside the KeyboardInterrupt exception.

Running the code longer than 15 seconds failed to respond to ctrl+c. If I press ctrl+c after 15 seconds, It is not working. It is not throwing KeyboardInterrupt exception. What could be the reason ? I tested this on Linux.

#!/usr/bin/python

import os, sys, threading, time

class Worker(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self)
    # A flag to notify the thread that it should finish up and exit
    self.kill_received = False

  def run(self):
      while not self.kill_received:
          self.do_something()

  def do_something(self):
      [i*i for i in range(10000)]
      time.sleep(1)

def main(args):

    threads = []
    for i in range(10):
        t = Worker()
        threads.append(t)
        t.start()

    while len(threads) > 0:
        try:
            # Join all threads using a timeout so it doesn't block
            # Filter out threads which have been joined or are None
            threads = [t.join(1) for t in threads if t is not None and t.isAlive()]
        except KeyboardInterrupt:
            print "Ctrl-c received! Sending kill to threads..."
            for t in threads:
                t.kill_received = True

if __name__ == '__main__':
  main(sys.argv)

2 Answers 2

3

After the first execution of

threads = [t.join(1) for t in threads if t is not None and t.isAlive()]

your variable threads contains

[None, None, None, None, None, None, None, None, None, None]

after the second execution, the same variable threads contains:

[]

At this point, len(threads) > 0 is False and you get out of the while loop. Your script is still running since you have 10 threads still active, but since you're not anymore in your try / except block (to catch KeyboardInterrupt), you can't stop using Ctrl + C

Add some prints to your script to see what I described:

#!/usr/bin/python

import os, sys, threading, time

class Worker(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self)
    # A flag to notify the thread that it should finish up and exit
    self.kill_received = False

  def run(self):
      while not self.kill_received:
          self.do_something()

  def do_something(self):
      [i*i for i in range(10000)]
      time.sleep(1)

def main(args):

    threads = []
    for i in range(10):
        t = Worker()
        threads.append(t)
        t.start()
        print('thread {} started'.format(i))

    while len(threads) > 0:
        print('Before joining')
        try:
            # Join all threads using a timeout so it doesn't block
            # Filter out threads which have been joined or are None
            threads = [t.join(1) for t in threads if t is not None and t.isAlive()]
            print('After join() on threads: threads={}'.format(threads))

        except KeyboardInterrupt:
            print("Ctrl-c received! Sending kill to threads...")
            for t in threads:
                t.kill_received = True
    print('main() execution is now finished...')

if __name__ == '__main__':
  main(sys.argv)

And the result:

$ python thread_test.py
thread 0 started
thread 1 started
thread 2 started
thread 3 started
thread 4 started
thread 5 started
thread 6 started
thread 7 started
thread 8 started
thread 9 started
Before joining
After join() on threads: threads=[None, None, None, None, None, None, None, None, None, None]
Before joining
After join() on threads: threads=[]
main() execution is now finished...

Actually, Ctrl + C doesn't stop to work after 15 seconds, but after 10 or 11 seconds. This is the time needed to create and start the 10 threads (less than a second) and to execute join(1) on each thread (about 10 seconds).

Hint from the doc:

As join() always returns None, you must call isAlive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

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

Comments

0

to follow up on the poster above, isAlive() got renamed to is_alive() tried on Python 3.9.6

full code:

#!/usr/bin/python

import os, sys, threading, time

class Worker(threading.Thread):
  def __init__(self):
    threading.Thread.__init__(self)
    # A flag to notify the thread that it should finish up and exit
    self.kill_received = False

  def run(self):
      while not self.kill_received:
          self.do_something()

  def do_something(self):
      [i*i for i in range(10000)]
      time.sleep(1)

def main(args):

    threads = []
    for i in range(10):
        t = Worker()
        threads.append(t)
        t.start()
        print('thread {} started'.format(i))

    while len(threads) > 0:
        print('Before joining')
        try:
            # Join all threads using a timeout so it doesn't block
            # Filter out threads which have been joined or are None
            threads = [t.join(1) for t in threads if t is not None and t.is_alive()]
            print('After join() on threads: threads={}'.format(threads))

        except KeyboardInterrupt:
            print("Ctrl-c received! Sending kill to threads...")
            for t in threads:
                t.kill_received = True
    print('main() execution is now finished...')

if __name__ == '__main__':
  main(sys.argv)

1 Comment

This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker.

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.