5

In the example of a asynchronous (threading) SocketServer http://docs.python.org/2/library/socketserver.html a server thread (called server_thread) is started, to start new threads for each request. Due to some problems catching KeyboardInterrupts, I started looking for similar code and found that there's no apparent difference when NOT using a server thread, but ctrl-c actually works.

Even though my code works I'd very much like to know

1) Why does not a simple 'try' to catch KeyboardInterrupt work, when using the server_thread?

2) What good does the server_thread from the example serve - as opposed to my somewhat simpler example?

From the python SocketServer example, catching keyboardinterrupt in try does not work:

if __name__ == "__main__":
    server = ThreadedTCPServer(serverAddr, SomeCode)
<snip>
    # Start a thread with the server -- that thread will then start one
    # more thread for each request
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.start()

My simpler example, ctrl-c works.

if __name__ == "__main__":
    server = ThreadedTCPServer(serverAddr, SomeCode)
    try:
        server.serve_forever()
        print "ctrl-c to exit"
    except KeyboardInterrupt:
        print  "interrupt received, exiting"
        server.shutdown() 
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
user135361
  • 125
  • 1
  • 6

2 Answers2

5

1) That's a general problem. When you do CTRL+C then a signal is sent to the process. In the process the main thread catches the signal and (if not handled properly) the main thread gets interrupted. But that signal does not kill other threads. And Python won't quit as long as there are non-daemon threads running (because that would not be safe). If you know what you are doing you can add this:

server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()

and it should work now (assuming you do something after server_thread.start() like waiting - otherwise Python will just quit, it won't wait for daemon threads). However remember that you may kill your server during some important operation. In order to avoid that you should implement some sort of graceful kill:

import signal

if __name__ == "__main__":
    server = ThreadedTCPServer(serverAddr, SomeCode)

    # some code
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.start()
    # some code

    try:
        signal.pause()  # wait for a signal, perhaps in a loop?
    except:
        server.shutdown()  # graceful quit

2) It just starts a server in a separate thread. Perhaps the idea was that you can do other operations in the meantime? If you only want to run the server there is no need to do that.

Also the reason might be the one I've stated above: graceful quit. If you just interrupt the server it will die, perhaps during some important operation.

freakish
  • 54,167
  • 9
  • 132
  • 169
  • You can interrupt the ThreadedTCPServer in the `serve_forever()`. It does nothing important. The important task could be in the handler threads which you may want to wait for. – User Mar 05 '14 at 14:50
1

In the server-thread example you start a new thread which serves the content. This thread is started not as deamon (deamon = False). This means that the program will not exit until the server_forever() is done. So the main-thread does nothing and the program waits for the non-deamon threads to close.

  1. The only difference I see is that this new thread is not the main-thread and thus does not handle the KeyboardInterrupt or other things that the main thread does.
  2. This could be useful if you want to integrate a GUI with your server. The GUI could run in parallel.
User
  • 14,131
  • 2
  • 40
  • 59
  • the 'print "ctrl-c to exit"' still executes though, so I would expect the try to work. If it just stays in the server_thread then the print shouldn't work, or am I wrong? – user135361 Mar 05 '14 at 12:12
  • Sorry, which print do you mean? Could you explain more? – User Mar 05 '14 at 14:57