Problem in multithreading Socketserver explained (should apply to *nix, too)
Michael Abbott
michael at rcp.co.uk
Wed Aug 29 11:28:02 EDT 2001
gerson.kurz at t-online.de (Gerson Kurz) wrote in
news:3b8c84e1.766674781 at news.isar.de:
> The BaseHTTPServer is derived from SocketServer.TCPServer.
> This class serves up data using the handle_request() member function.
> It looks like this (comments added by me):
>
> -----------------------
> def handle_request(self):
> try:
> request, client_address = self.get_request()
> except socket.error:
> return
> if self.verify_request(request, client_address):
> try:
> # this will start a new thread
> self.process_request(request, client_address)
> except:
> self.handle_error(request, client_address)
> # this will close the socket still in use by the new thread
> self.close_request(request)
> -----------------------
> The ThreadingTCPServer, which can be mixed in at a higher point in the
> hierarchy, overloads self.process_request() to startup a new thread
> for that request. However, once the thread has started, if the thread
> running self.handle_request() gets the CPU before the other thread has
> finished reading from the request, it will close the request, even
> while a thread serving the request is running in the background.
> And, self.close_request() closes the socket, so the error message
> about _sock being a number is completely explainable.
>
> So I don't think just adding ThreadingTCPServer could've worked from
> the start; and the same problem could possibly happen on the *nix
> implementation of ThreadingTCPServer, too.
>
> Solution:
>
> 1) the derived TCPServer has to implement close_request(self) as a
> stub function
> 2) the request class has to implement handle() and at the end of the
> function, close the socket.
>
This is very timely for me, many thanks for the diagnosis. I'm not sure I
agree with your solution, though. Surely all of the processing after the
request has been received should be split off into another thread?
Here's my solution:
class ThreadedHTTPServer(BaseHTTPServer.HTTPServer):
thread_pool = ThreadPool.ThreadPool(5, onThreadStartup)
# We override handle_request so that all of the processing for one
# request can be done in a separate thread.
# Unfortunately, we can't use the clever ThreadingMixIn technique,
# because the implementation of SocketServer.BaseServer is broken. In
# particular, close_request() can't be called until the spawned thread
# has completed processing!
def handle_request(self):
# This bit stolen wholesale from SocketServer
try:
request, client_address = self.get_request()
except socket.error:
return
# We do all the rest in its own thread
thread = threading.Thread(
target=self.handle_request_body,
args=(request, client_address))
thread.start()
# This part of the processing is run in its own thread
def handle_request_body(self, request, client_address):
# Again, this bit's thieved from SocketServer
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.close_request(request)
More information about the Python-list
mailing list