Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!news.glorb.com!newsfeed.xs4all.nl!newsfeed1.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.002 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'yet.': 0.04; 'handler': 0.05; 'newbie': 0.05; 'output': 0.05; '*not*': 0.07; 'correct.': 0.07; 'result,': 0.07; 'arguments': 0.09; 'defines': 0.09; 'exceeds': 0.09; 'here?': 0.09; 'option,': 0.09; 'skip:% 20': 0.09; 'cc:addr:python-list': 0.11; 'def': 0.12; 'thread': 0.14; 'assumptions': 0.16; 'block.': 0.16; 'blocking': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'handled.': 0.16; 'loop.': 0.16; 'mode,': 0.16; 'non-blocking': 0.16; 'query;': 0.16; 'res': 0.16; 'threads,': 0.16; 'throw': 0.16; 'timeout': 0.16; '(you': 0.16; 'exception': 0.16; 'wrote:': 0.18; 'wed,': 0.18; 'bit': 0.19; 'else,': 0.19; 'saying': 0.22; 'separate': 0.22; 'cc:addr:python.org': 0.22; 'certainly': 0.24; 'instance,': 0.24; 'tells': 0.24; 'fairly': 0.24; 'question': 0.24; 'cc:2**0': 0.24; 'query': 0.26; 'this:': 0.26; 'header:In- Reply-To:1': 0.27; 'idea': 0.28; 'on,': 0.29; 'quickly': 0.29; 'returned': 0.30; 'message-id:@mail.gmail.com': 0.30; 'code': 0.31; 'requests': 0.31; "skip:' 10": 0.31; 'usually': 0.31; '"do': 0.31; 'assumes': 0.31; 'block,': 0.31; 'fast.': 0.31; 'fine,': 0.31; 'idea,': 0.31; "they'll": 0.31; 'run': 0.32; 'third': 0.33; 'candidate': 0.34; 'screen': 0.34; 'skip:d 20': 0.34; 'could': 0.34; 'basic': 0.35; 'common': 0.35; 'something': 0.35; 'case,': 0.35; 'done.': 0.35; 'no,': 0.35; 'operations': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'there': 0.35; 'really': 0.36; 'done,': 0.36; 'done': 0.36; 'doing': 0.36; 'employee': 0.37; 'turn': 0.37; 'being': 0.38; 'remote': 0.38; 'system,': 0.38; 'server': 0.38; 'connections': 0.38; 'pm,': 0.38; 'that,': 0.38; 'does': 0.39; 'bad': 0.39; 'though,': 0.39; 'either': 0.39; 'how': 0.40; 'ensure': 0.60; 'most': 0.60; 'entire': 0.61; "you're": 0.61; 'back': 0.62; 'times': 0.62; "you've": 0.63; 'guarantee': 0.63; 'name': 0.63; 'soon': 0.63; 'skip:n 10': 0.64; 'more': 0.64; 'within': 0.65; 'frank': 0.68; 'periodically': 0.68; 'us,': 0.73; 'asynchronous': 0.84; 'coffee': 0.84; 'concept.': 0.84; 'distinguish': 0.84; "it'd": 0.84; 'water,': 0.84; 'can!': 0.91; 'response,': 0.91; 'whereas': 0.91; 'to:none': 0.92; 'hot': 0.96 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=FdR2n9d4/LTlhZwUxFteOGMHSZTj5UHRMwetTNr+/Jg=; b=tR9sUnrJQdXxngo0rCyIHlqLfGVKYn0hxrJy2cTZKf3gk4qq8NDW/bT6qFqUv9D3p1 XxMLd1scIo0UgpFzEb9BySqUtGUY13ZurVYtWVG1UBCtkTuA0HuRZgA8gRz6Q3ojRDh/ 2yxAeuhJIwTAfCnu6B7IqD3eCLG29PCmHTtPoHEyFNPFOm5YTgBKgK7qhkwrnnRcPd1w DsOfgCaXHBsfcQCk0dQPyIYYA51IYeArOn1u9rME0GDpQoOgaSZcElIAvfxSIpTFdy6/ 30y/2BQXDf0D7cMb/8bPdFRYl8AhqWNZ4fk4uBuP9bEYQHXJRop9KyVut3uZNXbAOPRt cVJw== MIME-Version: 1.0 X-Received: by 10.69.25.69 with SMTP id io5mr12569086pbd.22.1397051224982; Wed, 09 Apr 2014 06:47:04 -0700 (PDT) In-Reply-To: References: <87d2gt4td2.fsf@elektro.pacujo.net> <7xha651yx2.fsf@ruckus.brouhaha.com> <877g70wg8p.fsf@elektro.pacujo.net> Date: Wed, 9 Apr 2014 23:47:04 +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: 73 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1397051723 news.xs4all.nl 2941 [2001:888:2000:d::a6]:58251 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:69953 On Wed, Apr 9, 2014 at 11:23 PM, Frank Millman wrote: > Can I ask a newbie question here? You certainly can! > I understand that, if one uses threading, each thread *can* block without > affecting other threads, whereas if one uses the async approach, a request > handler must *not* block, otherwise it will hold up the entire process and > not allow other requests to be handled. That would be correct. > How does one distinguish betwen 'blocking' and 'non-blocking'? Is it > either/or, or is it some arbitrary timeout - if a handler returns within > that time it is non-blocking, but if it exceeds it it is blocking? No; a blocking request is one that waits until it has a response, and a non-blocking request is one that goes off and does something, and then comes back to you when it's done. When you turn on the kettle, you can either stay there and watch until it's ready to make your coffee (or, in my case, hot chocolate), or you can go away and come back when it whistles at you to say that it's boiling. A third option, polling, is when you put a pot of water on the stove, turn it on, and then come back periodically to see if it's boiling yet. As the old saying tells us, blocking I/O is a bad idea with pots of water, because it'll never return. > In my environment, most requests involve a database lookup. I endeavour to > ensure that a response is returned quickly (however one defines quickly) but > I cannot guarantee it if the database server is under stress. Is this a good > candidate for async, or not? No, that's a bad idea, because you have blocking I/O. If you have multiple threads, it's fine, because the thread that's waiting for the database will be blocked, and other threads can run (you may need to ensure that you have separate database connections for your separate threads); but in an asynchronous system, you want to be able to go and do something else while you're waiting. Something like this: def blocking_database_query(id): print("Finding out who employee #%d is..."%id) res = db.query("select name from emp where id=12345") print("Employee #%d is %s."%(id,res[0].name)) def nonblocking_query(id): print("Finding out who employee #%d is..."%id) def nextstep(res): print("Employee #%d is %s."%(id,res[0].name)) db.asyncquery(nextstep, "select name from emp where id=12345") This is a common way to do asynchronous I/O. Instead of saying "Do this and give me a result", you say "Do this, and when you have a result, call this function". Then as soon as you've done that, you return (to some main loop, probably). It's usually a bit more complicated than this (eg you might need multiple callbacks or additional arguments in case it times out or otherwise fails - there's no way to throw an exception into a callback, the way the blocking query could throw something instead of returning), but that's the basic concept. You may be able to get away with doing blocking operations in asynchronous mode, if you're confident they'll be fairly fast. But you have to be really REALLY confident, and it does create assumptions that can be wrong. For instance, the above code assumes that print() won't block. You might think "Duh, how can printing to the screen block?!?", but if your program's output is being piped into something else, it most certainly can :) If that were writing to a remote socket, though, it'd be better to perform those operations asynchronously too: attempt to write to the socket; once that's done, start the database query; when the database result arrives, write the response to the socket; when that's done, go back to some main loop. ChrisA