Path: csiph.com!usenet.pasdenom.info!news.etla.org!news.stack.nl!newsfeed.xs4all.nl!newsfeed3.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; '"this': 0.03; 'concurrently': 0.07; 'context': 0.07; 'method.': 0.07; 'none:': 0.07; 'executes': 0.09; 'linear': 0.09; 'mentions': 0.09; 'seen,': 0.09; 'sentence': 0.09; 'try:': 0.09; 'python': 0.11; 'def': 0.12; 'jan': 0.12; "(i'm": 0.16; 'clear.': 0.16; 'concurrent': 0.16; 'coroutines': 0.16; 'event-driven': 0.16; 'hypothetical': 0.16; 'indirectly': 0.16; 'paradigms': 0.16; 'readable': 0.16; 'sockets': 0.16; 'specifying': 0.16; 'task.': 0.16; 'yet)': 0.16; 'wrote:': 0.18; 'module': 0.19; 'trying': 0.19; 'examples': 0.20; 'written': 0.21; 'code,': 0.22; 'programming': 0.22; 'header:User- Agent:1': 0.23; 'decorators': 0.24; 'skip': 0.24; 'mon,': 0.24; 'versions': 0.24; "i've": 0.25; 'switch': 0.26; 'task': 0.26; 'code:': 0.26; 'header:In-Reply-To:1': 0.27; 'idea': 0.28; 'function': 0.29; 'correct': 0.29; 'raise': 0.29; 'related': 0.29; 'statement': 0.30; "i'm": 0.30; 'code': 0.31; 'easier': 0.31; 'factor': 0.31; 'yields': 0.31; 'run': 0.32; 'running': 0.33; 'implemented': 0.33; 'core': 0.34; 'could': 0.34; 'except': 0.35; 'something': 0.35; 'etc.)': 0.35; 'objects': 0.35; 'operations': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'really': 0.36; 'cancel': 0.36; 'yield': 0.36; 'charset:us-ascii': 0.36; 'possible': 0.36; 'list': 0.37; 'message-id:@gmail.com': 0.38; 'tasks': 0.38; 'to:addr:python-list': 0.38; 'track': 0.38; 'recent': 0.39; 'expect': 0.39; 'sure': 0.39; 'to:addr:python.org': 0.39; 'how': 0.40; 'future': 0.60; 'algorithms': 0.60; 'introduced': 0.61; 'first': 0.61; 'content- disposition:inline': 0.62; 'complete': 0.62; 'myself': 0.63; 'today': 0.64; 'more': 0.64; 'different': 0.65; 'between': 0.67; 'tasks.': 0.68; 'fact,': 0.69; 'overall': 0.69; 'manner': 0.72; '(open': 0.84; 'cancellation': 0.84; 'loose': 0.84; 'oscar': 0.84; 'picture.': 0.84; 'cancelled': 0.91; 'responses': 0.93 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:to:subject:message-id:references:mime-version :content-type:content-disposition:in-reply-to:user-agent; bh=rIU5UZfBlpVWBbRIv3qub/sZIwxhK4/PLXGhCppAUY0=; b=sdrqF5i4Czw3FnR9pkpyvEEFsOrJwxtAMc7MpSFR5IA02VbzrExmH3cK6OlVlKj0rT hy0/Ayh5BVY6tt55ztqttTn21B8xGU5TJXbkPgLmil6fx8VC+XzH5qSgIU04rJPUFV6B nf3GSVOh/yov7oMTRCeOtdFBNk2DfeTta1dFheKhx+bLV9xqQCERhRRLiAiHNKVBDexE 25RQdbONLCA4AwH5o4xZm8maaK3uwDHucnb9JoAAJf10R2B/cykgBSdcVKvNJ4tFuoPd EI+PGMPEeT8FNcvTalKEBAFfMZ5tm5ZQoAPnoZzTUmA58zM6CevrbfCfZ+I/qrNFHPPy CqOg== X-Received: by 10.180.206.138 with SMTP id lo10mr2151480wic.25.1389790710442; Wed, 15 Jan 2014 04:58:30 -0800 (PST) Date: Wed, 15 Jan 2014 12:58:26 +0000 From: Oscar Benjamin To: python-list@python.org Subject: Re: Trying to wrap my head around futures and coroutines References: <20140107022958.GA59457@cskk.homeip.net> <20140107024521.GA75679@cskk.homeip.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) 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: 72 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1389790712 news.xs4all.nl 2861 [2001:888:2000:d::a6]:55767 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:63980 On Mon, Jan 06, 2014 at 09:15:56PM -0600, Skip Montanaro wrote: > From the couple responses I've seen, I must have not made myself > clear. Let's skip specific hypothetical tasks. Using coroutines, > futures, or other programming paradigms that have been introduced in > recent versions of Python 3.x, can traditionally event-driven code be > written in a more linear manner so that the overall algorithms > implemented in the code are easier to follow? My code is not > multi-threaded, so using threads and locking is not really part of the > picture. In fact, I'm thinking about this now precisely because the > first sentence of the asyncio documentation mentions single-threaded > concurrent code: "This module provides infrastructure for writing > single-threaded concurrent code using coroutines, multiplexing I/O > access over sockets and other resources, running network clients and > servers, and other related primitives." > > I'm trying to understand if it's possible to use coroutines or objects > like asyncio.Future to write more readable code, that today would be > implemented using callbacks, GTK signals, etc. Hi Skip, I don't yet understand how asyncio works in complete examples (I'm not sure that many people do yet) but I have a loose idea of it so take the following with a pinch of salt and expect someone else to correct me later. :) With asyncio the idea is that you can run IO operations concurrently in the a single thread. Execution can switch between different tasks while each task can be written as a linear-looking generator function without the need for callbacks and locks. Execution switching is based on which task has avilable IO data. So the core switcher keeps track of a list of objects (open files, sockets etc.) and executes the task when something is available. >From the perspective of the task generator code what happens is that you yield to allow other code to execute while you wait for some IO e.g.: @asyncio.coroutine def task_A(): a1 = yield from futureA1() a2 = yield from coroutineA2(a1) # Indirectly yields from futures a3 = yield from futureA3(a2) return a3 At each yield statement you are specifying some operation that takes time. During that time other coroutine code is allowed to execute in this thread. If task_B has a reference to the future that task_A is waiting on then it can be cancelled with the Future.cancel() method. I think that you can also cancel with a reference to the task. So I think you can do something like @asyncio.coroutine def task_A(): # Cancel the other task and wait if ref_taskB is not None: ref_taskB.cancel() asyncio.wait([ref_taskB]) try: # Linear looking code with no callbacks a1 = yield from futureA1() a2 = yield from coroutineA2(a1) # Indirectly yields from futures a3 = yield from futureA3(a2) except CancelledError: stop_A() raise # Or return something... return a3 Then task_B() would have the reciprocal structure. The general form with more than just A or B would be to have a reference to the current task then you could factor out the cancellation code with context managers, decorators or something else. Oscar