120

I am confused as to why the following code snippet would not exit when called in the thread, but would exit when called in the main thread.

import sys, time
from threading import Thread

def testexit():
    time.sleep(5)
    sys.exit()
    print "post thread exit"

t = Thread(target = testexit)
t.start()
t.join()
print "pre main exit, post thread exit"
sys.exit()
print "post main exit"

The docs for sys.exit() state that the call should exit from Python. I can see from the output of this program that "post thread exit" is never printed, but the main thread just keeps on going even after the thread calls exit.

Is a separate instance of the interpreter being created for each thread, and the call to exit() is just exiting that separate instance? If so, how does the threading implementation manage access to shared resources? What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

vvvvv
  • 25,404
  • 19
  • 49
  • 81
Shabbyrobe
  • 12,298
  • 15
  • 60
  • 87

6 Answers6

93

sys.exit() raises the SystemExit exception, as does thread.exit(). So, when sys.exit() raises that exception inside that thread, it has the same effect as calling thread.exit(), which is why only the thread exits.

Richard
  • 56,349
  • 34
  • 180
  • 251
rpkelly
  • 2,076
  • 1
  • 20
  • 19
45

What if I did want to exit the program from the thread?

For Linux:

os.kill(os.getpid(), signal.SIGINT)

This sends a SIGINT to the main thread which raises a KeyboardInterrupt. With that you have a proper cleanup. Also you can register a handler, if you want to react differently.

For Windows:

The above does not work on Windows, as you can only send a SIGTERM signal, which is not handled by Python and has the same effect as os._exit().

The only option is to use:

os._exit()

This will exit the entire process without any cleanup. If you need cleanup, you need to communicate with the main thread in another way.

Chris
  • 2,461
  • 1
  • 23
  • 29
26

What if I did want to exit the program from the thread?

Apart from the method Deestan described you can call os._exit (notice the underscore). Before using it make sure that you understand that it does no cleanups (like calling __del__ or similar).

Helmut Grohne
  • 6,578
  • 2
  • 31
  • 67
15

What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

My preferred method is Erlang-ish message passing. Slightly simlified, I do it like this:

import sys, time
import threading
import Queue # thread-safe

class CleanExit:
  pass

ipq = Queue.Queue()

def testexit(ipq):
  time.sleep(5)
  ipq.put(CleanExit)
  return

threading.Thread(target=testexit, args=(ipq,)).start()
while True:
  print "Working..."
  time.sleep(1)
  try:
    if ipq.get_nowait() == CleanExit:
      sys.exit()
  except Queue.Empty:
    pass
Deestan
  • 16,738
  • 4
  • 32
  • 48
  • 5
    You don't need a `Queue` here. Just a simple `bool` will do fine. The classic name for this variable is `is_active` and its initial default value is `True`. – Asclepius Feb 21 '13 at 23:36
  • 4
    Yes, you are correct. According to http://effbot.org/zone/thread-synchronization.htm , modifying a `bool` (or any other atomic operation) will do perfectly for this particular problem. The reason I go with `Queue`s is that when working with threaded agents I tend to end up needing several different signals (`flush`, `reconnect`, `exit`, etc...) almost immediately. – Deestan Feb 22 '13 at 11:46
  • 1
    Deestan: Since a `bool` would work, as @Acumenus pointed out, then likewise a simple `int` would seem to be all that's needed to be able handle several different signals — `bool` is just a subclass of `int`, after all. – martineau Oct 28 '20 at 11:46
14

Is the fact that "pre main exit, post thread exit" is printed what's bothering you?

Unlike some other languages (like Java) where the analog to sys.exit (System.exit, in Java's case) causes the VM/process/interpreter to immediately stop, Python's sys.exit just throws an exception: a SystemExit exception in particular.

Here are the docs for sys.exit (just print sys.exit.__doc__):

Exit the interpreter by raising SystemExit(status).
If the status is omitted or None, it defaults to zero (i.e., success).
If the status is numeric, it will be used as the system exit status.
If it is another kind of object, it will be printed and the system
exit status will be one (i.e., failure).

This has a few consequences:

  • in a thread it just kills the current thread, not the entire process (assuming it gets all the way to the top of the stack...)
  • object destructors (__del__) are potentially invoked as the stack frames that reference those objects are unwound
  • finally blocks are executed as the stack unwinds
  • you can catch a SystemExit exception

The last is possibly the most surprising, and is yet another reason why you should almost never have an unqualified except statement in your Python code.

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
4

_thread.interrupt_main() is available since Python 3.7 (optional before that)

psorenson
  • 259
  • 2
  • 5