Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!news.mixmin.net!feeds.phibee-telecom.net!newsfeed.xs4all.nl!newsfeed2.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'yet.': 0.04; 'handler': 0.05; 'python)': 0.05; '"""': 0.07; 'socket': 0.07; 'string': 0.09; 'data):': 0.09; 'http': 0.09; 'protocols,': 0.09; 'skip:/ 10': 0.09; 'tcp/ip': 0.09; 'user-agent:': 0.09; 'cc:addr:python- list': 0.11; 'python': 0.11; 'def': 0.12; 'gui': 0.12; 'assume': 0.14; 'wrote': 0.14; 'thread': 0.14; '"""get': 0.16; 'all?': 0.16; 'block.': 0.16; 'blocking': 0.16; 'closed.': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'host:': 0.16; 'it),': 0.16; 'loop.': 0.16; 'mode,': 0.16; 'non- blocking': 0.16; 'readable': 0.16; 'tcp,': 0.16; 'will:': 0.16; 'world!': 0.16; 'appropriate': 0.16; 'wrote:': 0.18; 'library': 0.18; 'bit': 0.19; 'basically': 0.19; 'connects': 0.19; 'passing': 0.19; 'result.': 0.19; 'server,': 0.19; 'slightly': 0.19; 'thu,': 0.19; 'solution.': 0.20; 'seems': 0.21; '(in': 0.22; 'cc:addr:python.org': 0.22; 'creating': 0.23; 'parse': 0.24; 'sends': 0.24; '(or': 0.24; 'cc:2**0': 0.24; 'handling': 0.26; 'this:': 0.26; 'header:In-Reply-To:1': 0.27; "doesn't": 0.30; 'message-id:@mail.gmail.com': 0.30; '(which': 0.31; 'getting': 0.31; 'requests': 0.31; '(usually': 0.31; 'block,': 0.31; 'convenience': 0.31; 'follows': 0.31; 'lot.': 0.31; 'request,': 0.31; 'there.': 0.32; 'open': 0.33; 'becomes': 0.33; 'level.': 0.33; 'skip:d 20': 0.34; "can't": 0.35; 'connection': 0.35; 'something': 0.35; 'operations': 0.35; 'usual': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'add': 0.35; 'there': 0.35; 'version': 0.36; 'so,': 0.37; 'server': 0.38; 'process,': 0.38; 'writes': 0.38; 'handle': 0.38; 'whatever': 0.38; 'pm,': 0.38; 'that,': 0.38; 'does': 0.39; 'itself': 0.39; 'enough': 0.39; 'either': 0.39; 'ensure': 0.60; 'read': 0.60; 'most': 0.60; 'lower': 0.61; 'new': 0.61; 'effective': 0.61; 'entire': 0.61; 'information,': 0.61; 'simple': 0.61; 'you.': 0.62; 'back': 0.62; 'notified': 0.63; 'soon': 0.63; 'skip:n 10': 0.64; 'more': 0.64; 'different': 0.65; 'effectively': 0.66; 'worth': 0.66; 'frank': 0.68; 'request.': 0.70; 'listening': 0.74; 'potentially': 0.81; 'around,': 0.84; 'asynchronous': 0.84; 'pike': 0.84; 'way)': 0.84; 'to:none': 0.92; 'connection,': 0.95 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=u8J8UXVh7Q5EG9NUBH4Hq+/7GQE2cwmXTRW7QCZaYXI=; b=k9UWcvWDKgV+Pi0OvTaVGLD5pYYffvfi6mQnLVpIfuAIVhLtkjMuVefco6jg24i7ga WvC7YAwLU9QP0J5XI7SYDpcRDdVRotrm/Np2Qdcoz9yOZXTj3OqXHvywyqWWBSH9NZG4 D6AwI9fv2zvLd7vGK3k0459h4N2VWMXFpoxsugRCnWCsocnKpIEYUQRFDgomlbro89uO hKg0EcSYy09+OhlrYDq7OqYKLA1xBwf6MzrFzXjGs/vyK0qCqPFOLLzV2GI7fEllAPad hv2EAZibH+2iygSlRrgVYoXnJNWsxgjyfT8hy6c1DURpysdKnKs9MUmKUIpBI7OgOhRn kJHA== MIME-Version: 1.0 X-Received: by 10.68.191.138 with SMTP id gy10mr1320581pbc.169.1397122857176; Thu, 10 Apr 2014 02:40:57 -0700 (PDT) In-Reply-To: References: <87d2gt4td2.fsf@elektro.pacujo.net> <7xha651yx2.fsf@ruckus.brouhaha.com> <877g70wg8p.fsf@elektro.pacujo.net> Date: Thu, 10 Apr 2014 19:40:57 +1000 Subject: Re: threading From: Chris Angelico Cc: "python-list@python.org" Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 78 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1397122866 news.xs4all.nl 2835 [2001:888:2000:d::a6]:42076 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:70028 On Thu, Apr 10, 2014 at 7:17 PM, Frank Millman wrote: > The current version of my program uses HTTP. As I understand it, a client > makes a connection and submits a request. The server processes the request > and returns a result. The connection is then closed. > > In this scenario, does async apply at all? There is no open connection to > 'select' or 'poll'. You have to ensure that the request handler does not > block the entire process, so that the main loop is ready to accept more > connections. But passing the request to a thread for handling seems an > effective solution. Let's take this to a slightly lower level. HTTP is built on top of a TCP/IP socket. The client connects (usually on port 80), and sends a string like this: """GET /foo/bar/asdf.html HTTP/1.0 Host: www.spam.org User-Agent: Mozilla/5.0 """ The server then sends back something like this: """HTTP/1.0 200 OK Content-type: text/html Hello, world! """ These are carried on a straight-forward bidirectional stream socket, so the write and read operations (or send and recv, either way) can potentially block. With a small request, you can kinda assume that the write won't block, but the read most definitely will: it'll block until the server writes something for you. So it follows the usual model of blocking vs non-blocking. In blocking mode, you do something like this: data = socket.read() and it waits until it has something to return. In non-blocking mode, you do something like this: def data_available(socket, data): # whatever socket.set_read_callback(data_available) An HTTP handling library can then build a non-blocking request handler on top of that, by having data_available parse out the appropriate information, and return if it doesn't have enough content yet. So it follows the same model; you send off the request (and don't wait for it), and then get notified when the result is there. When you write the server, you effectively have the same principle, with one additional feature: a listening socket becomes readable whenever someone connects. So you can select() on that socket, just like you can with the others, and whenever there's a new connection, you add it to the collection and listen for requests on all of them. It's basically the same concept; as soon as you can accept a new connection, you do so, and then go back to the main loop. It's pretty simple when you let a lower-level library do the work for you :) The neat thing is, you can put all of this into a single program; I can't demo it in Python for you, but I have a Pike kernel that I wrote for my last job, which can handle a variety of different asynchronous operations: TCP, UDP (which just sends single packets, normally), a GUI (in theory), timers, the lot. It has convenience features for creating a DNS server, an HTTP server, and a stateful line-based server (covers lots of other protocols, like SMTP). And (though this bit would be hard to port to Python) it can update itself without shutting down. Yes, it can take some getting your head around, but it's well worth it. ChrisA