Re: Asyncio -- delayed calculation
On Sat, Dec 3, 2016 at 1:26 AM, Frank Millmanwrote: > Then Twisted made a strong case for an asynchronous approach. One of their > claims (which I have no reason to doubt) was that, because each user > 'session' spends most of its time waiting for something - keyboard input, > reply from database, etc - their approach allows hundreds of concurrent > users, something that I believe would not be possible with threading or > multi-processing. I'm not sure that "hundreds" would be a problem - I've had processes with hundreds of threads before - but if you get up to hundreds of *thousands* of threads, then yes, threads start to be a problem. When you have that sort of traffic (that's multiple requests per millisecond, or over a million requests per minute), you have to think about throughput and efficiency, not just simplicity. But for low traffic environments (single digits of requests per second), threads work just fine. Actually, for traffic levels THAT low, you could probably serialize your requests and nobody would notice. That's the simplest of all, but it scales pretty terribly :) Not everyone needs the full power of asyncio, so not everyone will really see the point. I like that it's available to everyone, though. If you need it, it's there. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
"Frank Millman": > Then Twisted made a strong case for an asynchronous approach. One of > their claims (which I have no reason to doubt) was that, because each > user 'session' spends most of its time waiting for something - > keyboard input, reply from database, etc - their approach allows > hundreds of concurrent users, something that I believe would not be > possible with threading or multi-processing. You don't need asyncio to do event-driven programming in Python. I have programmed several event-driven applications in Python (and even more of them in other languages) without using asyncio (you only need select.epoll()). My favorite model is the so-called "callback hell" with explicit finite state machines. That tried-and-true model has always been used with parallel, concurrent and network programming as well as user-interface programming. Your code should closely resemble this: http://www.bayfronttechnologies.com/capesdl.gif> Multiprocessing is also an important tool for compartmentalizing and parallelizing functionality. Threads are mainly suitable for turning obnoxious blocking APIs into nonblocking ones, but even there, I would prefer to give that job to separate processes. Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
"Steve D'Aprano" wrote in message news:58417e2d$0$1612$c3e8da3$54964...@news.astraweb.com... My first impressions on this is that we have a couple of good models for preemptive parallelism, threads and processes, both of which can do everything that concurrency can do, and more, and both of which are significantly easier to understand too. So why do we need asyncio? What is it actually good for? As I have mentioned, my use-case is a multi-user client/server system. The traditional approach used to be to use threads to allow multiple users to work concurrently. Then Twisted made a strong case for an asynchronous approach. One of their claims (which I have no reason to doubt) was that, because each user 'session' spends most of its time waiting for something - keyboard input, reply from database, etc - their approach allows hundreds of concurrent users, something that I believe would not be possible with threading or multi-processing. Frank Millman -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Steve D'Aprano: > py> await x > File "", line 1 > await x > ^ > SyntaxError: invalid syntax "await" is only allowed inside a coroutine. > So why do we need asyncio? What is it actually good for? Asyncio is a form of cooperative multitasking. It presents a framework of "fake threads". The objective and programming model is identical to that of threads. As to why Python should need a coroutine framework, you can answer it from two angles: 1. Why does one need cooperative multitasking? 2. Why should one use coroutines to implement cooperative multitasking? 1. Cooperative multitasking has made its way in Java ("NIO") and C# (async/await). It has come about as enterprise computing realized the multithreading model of the 1990's was shortsighted. In particular, it wasn't scalable. Enterprise solutions collapsed under the weight of tens of thousands of threads. Stack space ran out and schedulers became slow. https://en.wikipedia.org/wiki/C10k_problem> 2. I have always been into asynchronous programming (cooperative multitasking), but coroutines are far from my favorite programming model. I am guessing Guido introduced them to Python because: * C# has them (me too!). * They have a glorious computer scientific past (CSP, emperor's new I/O framework). * They look like threads. * They were already there in the form of generators (low-hanging fruit). And, maybe most importantly: * Twisted (et al) had needed an event-driven framework but Python didn't have one out of the box (https://lwn.net/Articles/692254/>). Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Thu, 1 Dec 2016 06:53 pm, Christian Gollwitzer wrote: > well that works - but I think it it is possible to explain it, without > actually understanding what it does behind the scences: > > x = foo() > # schedule foo for execution, i.e. put it on a TODO list > > await x > # run the TODO list until foo has finished Nope, sorry, that doesn't help even a little bit. For starters, it doesn't work: it is a syntax error. py> async def foo(): ... return 1 ... py> x = foo() py> await x File "", line 1 await x ^ SyntaxError: invalid syntax But even if it did work, why am I waiting for the TODO list to finish? Doesn't that mean I'm now blocking, which goes completely against the idea of writing non-blocking asynchronous code? If I wanted to block waiting for x, I'd just make it a regular, easy-to-understand, synchronous function. Besides, where does x stash it's result? In a global variable? What if another async routine messes with the same global? My first impressions on this is that we have a couple of good models for preemptive parallelism, threads and processes, both of which can do everything that concurrency can do, and more, and both of which are significantly easier to understand too. So why do we need asyncio? What is it actually good for? -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Thu, Dec 1, 2016 at 12:53 AM, Christian Gollwitzerwrote: > well that works - but I think it it is possible to explain it, without > actually understanding what it does behind the scences: > > x = foo() > # schedule foo for execution, i.e. put it on a TODO list This implies that if you never await foo it will still get done at some point (e.g. when you await something else), which for coroutines would be incorrect unless you call ensure_future() on it. Come to think about it, it would probably not be a bad style rule to consider that when you call something that returns an awaitable, you should always either await or ensure_future it (or something else that depends on it). Barring the unusual case where you want to create an awaitable but *not* immediately schedule it. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Am 30.11.16 um 22:07 schrieb Gregory Ewing: Chris Angelico wrote: That's because you're not actually running anything concurrently. Yes, I know what happens and why. My point is that for someone who *doesn't* know, simplistic attempts to explain what "await" means can be very misleading. There doesn't seem to be any accurate way of summarising it in a few words. The best we can do seems to be to just say "it's a magic word that you have to put in front of any call to a function that you defined as async". well that works - but I think it it is possible to explain it, without actually understanding what it does behind the scences: x = foo() # schedule foo for execution, i.e. put it on a TODO list await x # run the TODO list until foo has finished IMHO coroutines are a simple concept in itself, just that stackful programming (call/return) has tainted our minds so much that we have trouble figuring out a "function call" which does not "return" in the usual sense. The implementation is even more convoluted with the futures and promises and whatnot. For simply using that stuff it is not important to know how it works. Christian -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Gregory Ewing: > My point is that for someone who *doesn't* know, simplistic attempts > to explain what "await" means can be very misleading. > > There doesn't seem to be any accurate way of summarising it in a few > words. The best we can do seems to be to just say "it's a magic word > that you have to put in front of any call to a function that you > defined as async". I don't think it needs any other explanation. (Thousands of debugging hours will likely be spent locating the missing "await" keywords.) > I think it's possible to build a conceptual model that's easier to > grasp and hides more of the underlying machinery. I'm thinking the problem with asyncio is the very fact that it is hiding the underlying callbacks. I'm also predicting there will be quite a bit of asyncio code that ironically converts coroutines back into callbacks. > PEP 3152 was my attempt at doing that. Unfortunately, we seem to have > gone down a fork in the road that leads somewhere else and from which > there's no getting back. Alea jacta est. Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Terry Reedy: > On 11/30/2016 7:53 AM, Chris Angelico wrote: > >> I also think that everyone should spend some time writing >> multithreaded code before switching to asyncio. It'll give you a >> better appreciation for what's going on. > > I so disagree with this. I have written almost no thread code but have > successfully written asyncio and async await code. Perhaps this is > because I have written tkinter code, including scheduling code with > root.after. The tk and asyncio event loops and scheduling are quite > similar. I kinda agree with Chris in that asyncio is a programming model virtually identical with multithreading. Asyncio is sitting on an event-driven machinery, but asyncio does its best to hide that fact. The key similarity between coroutines and threads is concurrent linear sequences of execution that encode states as blocking function calls. Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Chris Angelico wrote: That's because you're not actually running anything concurrently. Yes, I know what happens and why. My point is that for someone who *doesn't* know, simplistic attempts to explain what "await" means can be very misleading. There doesn't seem to be any accurate way of summarising it in a few words. The best we can do seems to be to just say "it's a magic word that you have to put in front of any call to a function that you defined as async". A paraphrasing of the Zen comes to mind: "If the semantics are hard to explain, it may be a bad idea." > there are complexities to the underlying concepts that can't be hidden by any framework. I don't entirely agree with that. I think it's possible to build a conceptual model that's easier to grasp and hides more of the underlying machinery. PEP 3152 was my attempt at doing that. Unfortunately, we seem to have gone down a fork in the road that leads somewhere else and from which there's no getting back. -- Greg -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Wed, Nov 30, 2016 at 10:58 AM, Chris Angelicowrote: > On Thu, Dec 1, 2016 at 5:54 AM, Terry Reedy wrote: >> On 11/30/2016 7:53 AM, Chris Angelico wrote: >> >>> I also think that everyone should spend some time writing >>> multithreaded code before switching to asyncio. It'll give you a >>> better appreciation for what's going on. >> >> >> I so disagree with this. I have written almost no thread code but have >> successfully written asyncio and async await code. Perhaps this is because >> I have written tkinter code, including scheduling code with root.after. The >> tk and asyncio event loops and scheduling are quite similar. > > Okay, so maybe not everyone needs threading, but certainly having a > background in threading can help a lot. It _is_ possible to jump > straight into asyncio, but if you haven't gotten your head around > concurrency in other forms, you're going to get very much confused. > > Or maybe it's that the benefits of threaded programming can also be > gained by working with event driven code. That's also possible. I've dealt with multi-process (Python; some in C++ and C#), multi-thread (Python, C++, C#, and other), and co-routine concurrency (C#/Unity; I haven't used asyncio yet), and co-routine is by far the simplest, as you don't have to deal with locking or race conditions in general. If you understand threading/multi-process programming, co-routines will be easier to understand, as some of the aspects still apply. Learning threading/process-based will be easier in some ways if you know co-routine. In other ways, it will be harder as you'll have to learn to consider locking and race conditions in coding. I think in a venn-diagram of the complexity, co-routines would exist in a circle contained by multi-process (only explicit shared memory simplifies things, but there are still issues), which is inside a circle by threading. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Thu, Dec 1, 2016 at 5:54 AM, Terry Reedywrote: > On 11/30/2016 7:53 AM, Chris Angelico wrote: > >> I also think that everyone should spend some time writing >> multithreaded code before switching to asyncio. It'll give you a >> better appreciation for what's going on. > > > I so disagree with this. I have written almost no thread code but have > successfully written asyncio and async await code. Perhaps this is because > I have written tkinter code, including scheduling code with root.after. The > tk and asyncio event loops and scheduling are quite similar. Okay, so maybe not everyone needs threading, but certainly having a background in threading can help a lot. It _is_ possible to jump straight into asyncio, but if you haven't gotten your head around concurrency in other forms, you're going to get very much confused. Or maybe it's that the benefits of threaded programming can also be gained by working with event driven code. That's also possible. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On 11/30/2016 7:53 AM, Chris Angelico wrote: I also think that everyone should spend some time writing multithreaded code before switching to asyncio. It'll give you a better appreciation for what's going on. I so disagree with this. I have written almost no thread code but have successfully written asyncio and async await code. Perhaps this is because I have written tkinter code, including scheduling code with root.after. The tk and asyncio event loops and scheduling are quite similar. -- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Wed, Nov 30, 2016 at 11:40 PM, Gregory Ewingwrote: > Chris Angelico wrote: >> >> From the point of view of >> the rest of Python, no. It's a sign saying "Okay, Python, you can >> alt-tab away from me now". > > > The problem with that statement is it implies that if > you omit the "await", then the thing you're calling > will run uninterruptibly. Whereas what actually happens > is that it doesn't get run at all. That's because you're not actually running anything concurrently. Until you wait for something to be done, it doesn't start happening. Asynchronous I/O gives the illusion of concurrency, but actually, everything's serialized. I think a lot of the confusion around asyncio comes from people not understanding the fundamentals of what's going on; there are complexities to the underlying concepts that can't be hidden by any framework. No matter what you do, you can't get away from them. They're not asyncio's fault. They're not async/await's fault. They're not the fault of having forty-two thousand different ways to do things. They're fundamentals. I also think that everyone should spend some time writing multithreaded code before switching to asyncio. It'll give you a better appreciation for what's going on. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Chris Angelico wrote: From the point of view of the rest of Python, no. It's a sign saying "Okay, Python, you can alt-tab away from me now". The problem with that statement is it implies that if you omit the "await", then the thing you're calling will run uninterruptibly. Whereas what actually happens is that it doesn't get run at all. -- Greg -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tuesday 29 November 2016 14:21, Chris Angelico wrote: "await" means "don't continue this function until that's done". It blocks the function until a non-blocking operation is done. That explanation gives the impression that it's some kind of "join" operation on parallel tasks, i.e. if you do x = foo() do_something_else() y = await x then foo() somehow proceeds in the background while do_something_else() is going on. But that's not the way it works at all. -- Greg -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Wed, 30 Nov 2016 05:41 am, Ian Kelly wrote: > You mean how do you create something that can be awaited that doesn't > await something else in turn? With a Future. > > import asyncio > > class Awaitable(asyncio.Future): > def wake_up_later(self): > asyncio.get_event_loop().call_later(3, self.set_result, 42) Not to be confused with concurrent.Futures. https://docs.python.org/3.5/library/concurrent.futures.html https://docs.python.org/3.5/library/asyncio-task.html#asyncio.Future -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Mon, Nov 28, 2016 at 10:42 PM, Chris Angelicowrote: > On Tue, Nov 29, 2016 at 4:13 PM, Paul Rubin wrote: >> >> I haven't gotten my head around Python asyncio and have been wanting >> to read this: >> >>http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/ > > It's talking a lot about how we got here, which isn't all necessary if > you just want to give asyncio a whirl. The conclusion at the end says > that you should just use 'async def' and not bother with all the older > forms, which I agree with (subject to the usual caveat that this > implies no support for older Pythons). > > There's one thing that I really struggle with, though, and that's that > there's no easy and obvious way to demonstrate the lowest level of > operation. If "await x()" is like "yield from x()", how do you do the > innermost "yield" that actually does something? I have the same > confusion with Node.js, too. It's as if async primitives can't be > implemented in application code at all, they just have to be given to > you. Certainly it's not something made clear anywhere in the docs that > I've found. You mean how do you create something that can be awaited that doesn't await something else in turn? With a Future. import asyncio class Awaitable(asyncio.Future): def wake_up_later(self): asyncio.get_event_loop().call_later(3, self.set_result, 42) async def main(): awaitable = Awaitable() awaitable.wake_up_later() print(await awaitable) asyncio.get_event_loop().run_until_complete(main()) The trick is in arranging for the future's result to be set. For I/O events you would typically do that by associating a callback with a file descriptor on the event loop: https://docs.python.org/3/library/asyncio-eventloop.html#watch-file-descriptors If you need to do something particulary abstruse you can always write a custom Selector or event loop: https://docs.python.org/3/library/selectors.html#module-selectors https://docs.python.org/3/library/asyncio-eventloop.html#base-event-loop -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Gregory Ewing: > All the terminology around async/await is inherently confusing and > counterintuitive, IMO. I'm disappointed that we've ended up here. I think the conceptual mess can be clarified over time. Coroutines are essentially threads. Why Python needs two threading implementations is questionable. At least, with threads, you don't have to remember to tag your function definitions with "async" and your functions calls with "await". Both threads and now coroutines were introduced as fig leafs to cover the underlying finite state machines. Personally, I think it is a mistake to hide the state machines. Instead, people should learn to think in terms of state machines, which is the classic network protocol model. Marko -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tue, Nov 29, 2016 at 4:32 PM, Steven D'Apranowrote: > On Tuesday 29 November 2016 14:21, Chris Angelico wrote: > >> On Tue, Nov 29, 2016 at 1:23 PM, Steve D'Aprano >> wrote: >>> This is confusing: why is this awaiting something inside an async function? >>> Doesn't that mean that the await asyncio.gather(...) call is turned >>> blocking? >> >> "await" means "don't continue this function until that's done". It >> blocks the function until a non-blocking operation is done. > > So that would be... "yes"? >From the point of view of the function, yes. From the point of view of the rest of Python, no. It's a sign saying "Okay, Python, you can alt-tab away from me now". ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tue, Nov 29, 2016 at 4:13 PM, Paul Rubinwrote: > > I haven't gotten my head around Python asyncio and have been wanting > to read this: > >http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/ It's talking a lot about how we got here, which isn't all necessary if you just want to give asyncio a whirl. The conclusion at the end says that you should just use 'async def' and not bother with all the older forms, which I agree with (subject to the usual caveat that this implies no support for older Pythons). There's one thing that I really struggle with, though, and that's that there's no easy and obvious way to demonstrate the lowest level of operation. If "await x()" is like "yield from x()", how do you do the innermost "yield" that actually does something? I have the same confusion with Node.js, too. It's as if async primitives can't be implemented in application code at all, they just have to be given to you. Certainly it's not something made clear anywhere in the docs that I've found. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tuesday 29 November 2016 14:21, Chris Angelico wrote: > On Tue, Nov 29, 2016 at 1:23 PM, Steve D'Aprano >wrote: >> This is confusing: why is this awaiting something inside an async function? >> Doesn't that mean that the await asyncio.gather(...) call is turned >> blocking? > > "await" means "don't continue this function until that's done". It > blocks the function until a non-blocking operation is done. So that would be... "yes"? -- Steven "Ever since I learned about confirmation bias, I've been seeing it everywhere." - Jon Ronson -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Chris Angelicowrites: > Asynchronous I/O is something to get your head around I'd much > rather work with generator-based async functions... I haven't gotten my head around Python asyncio and have been wanting to read this: http://lucumr.pocoo.org/2016/10/30/i-dont-understand-asyncio/ Is that picture too bleak? I've used traditional cooperative multitasking systems in the past, and the main hazard with them is to be careful to not run for too long without yielding. Python's stuff seems much more complicated, with weirder hazards, some stemming from the hidden mutable state in every generator. I wonder whether a stackless-based green thread approach might have been simpler. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
To be fair, in other languages, such as C# or C++ with similar mechanisms, if you don't ask for the result from an async or future task, there's no guarantee the async task will be executed at all unless (or until) you ask for the result. C++'s futures even give an explicit flag indicating you want the task to be executed on the current thread, but deferred until the result is asked for. I'm sure the people on the standards committee had a rationale for this, but I see only marginal utility for that use case (maybe such as a cancellable expensive calc?). Seems like "I might need the result of this calc down the road, but I don't need it currently, so I'll just kick the can down the road.". I'm not aware of a mechanism to make C# behave that way, but I know it's possible that tasks will not execute until they're asked for their result, but I don't know that it is 100% deterministic. To be honest, I think async/await stile programming is so new that there's not a lot of good material about how to properly use it (as far as both when and how). The limit of my experience has been in C# dealing with web requests, where the usage is basically, I'm going to need this data, so I'll fire off the async request for it, and I've got a lot of work I can do before I need the result, so lets do that, until I need to wait (await) for the result of that request before I can proceed any further. It can be useful, but it is a new, completely different paradigm and methodology to wrap ones head around. I've used it for about 2 years in C# land, and I still don't know that I'm doing it right. I've not done any async/await in Python space, but I'm eager to learn and try, when it may be appropriate, But, most of my current use cases don't require it. On Mon, Nov 28, 2016 at 10:37 PM, Chris Angelicowrote: > On Tue, Nov 29, 2016 at 3:16 PM, Gregory Ewing > wrote: > > Chris Angelico wrote: > >> > >> "await" means "don't continue this function until that's done". It > >> blocks the function until a non-blocking operation is done. > > > > > > However, *not* using 'await' doesn't mean the operation > > will be done without blocking. Rather, it won't be done > > at all (and is usually an error, but there's no way for > > the implementation to detect it at the time). > > > > I don't blame Steve for being confused. All the > > terminology around async/await is inherently confusing > > and counterintuitive, IMO. I'm disappointed that we've > > ended up here. > > Asynchronous I/O is something to get your head around. As someone who > teaches both JavaScript and Python, I've run into this quite a bit, > and frankly, callbacks may be easy to explain at a concrete level, but > I'd much rather work with generator-based async functions (which JS is > getting soon too). The best way to think about it IMO is a "process" > that does blocking calls, and which the "system" can multitask away > from any time it's blocked. Your function awaits something, but Python > doesn't. > > ChrisA > -- > https://mail.python.org/mailman/listinfo/python-list > -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tue, Nov 29, 2016 at 3:16 PM, Gregory Ewingwrote: > Chris Angelico wrote: >> >> "await" means "don't continue this function until that's done". It >> blocks the function until a non-blocking operation is done. > > > However, *not* using 'await' doesn't mean the operation > will be done without blocking. Rather, it won't be done > at all (and is usually an error, but there's no way for > the implementation to detect it at the time). > > I don't blame Steve for being confused. All the > terminology around async/await is inherently confusing > and counterintuitive, IMO. I'm disappointed that we've > ended up here. Asynchronous I/O is something to get your head around. As someone who teaches both JavaScript and Python, I've run into this quite a bit, and frankly, callbacks may be easy to explain at a concrete level, but I'd much rather work with generator-based async functions (which JS is getting soon too). The best way to think about it IMO is a "process" that does blocking calls, and which the "system" can multitask away from any time it's blocked. Your function awaits something, but Python doesn't. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Chris Angelico wrote: "await" means "don't continue this function until that's done". It blocks the function until a non-blocking operation is done. However, *not* using 'await' doesn't mean the operation will be done without blocking. Rather, it won't be done at all (and is usually an error, but there's no way for the implementation to detect it at the time). I don't blame Steve for being confused. All the terminology around async/await is inherently confusing and counterintuitive, IMO. I'm disappointed that we've ended up here. -- Greg -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Mon, Nov 28, 2016 at 6:48 AM, Steve D'Apranowrote: > What am I doing wrong? Give yourself a bit more to debug with, since you're going to want to do something with the result your expensive calculation anyway: import asyncio class Counter: def __init__(self, i): self.count = 10 self.i = i async def count_down(self): print(self, self.i, "starting") while self.count > 0: # simulate a computation await asyncio.sleep(0.5) self.count -= 1 print(self, self.i, "completed") return self.i + self.count async def main(): pool = [Counter(i) for i in range(5)] results = [] for obj in pool: results.append(obj.count_down()) return results loop = asyncio.get_event_loop() print(loop.run_until_complete(main())) This gives you: [, , , , ] asynctest.py:25: RuntimeWarning: coroutine 'Counter.count_down' was never awaited print(loop.run_until_complete(main())) Ok, so let's fix that by adding an 'await' on line 21 (it's reported at line 25 because that's when the unawaited coroutines are gc'd): results.append(await obj.count_down()) Running that gives: <__main__.Counter object at 0x10203f978> 0 starting <__main__.Counter object at 0x10203f978> 0 completed <__main__.Counter object at 0x1025af710> 1 starting <__main__.Counter object at 0x1025af710> 1 completed <__main__.Counter object at 0x1025b60b8> 2 starting <__main__.Counter object at 0x1025b60b8> 2 completed <__main__.Counter object at 0x1025b60f0> 3 starting <__main__.Counter object at 0x1025b60f0> 3 completed <__main__.Counter object at 0x1025b6128> 4 starting <__main__.Counter object at 0x1025b6128> 4 completed [0, 1, 2, 3, 4] Still not right, only one count_down is run at a time. But that's because we're using a synchronous for loop to await our results and populate the results list. Naively, I tried an 'async for', but that's trying to be asynchronous in the wrong place: Traceback (most recent call last): File "asynctest.py", line 25, in print(loop.run_until_complete(main())) File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete return future.result() File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result raise self._exception File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step result = coro.send(None) File "asynctest.py", line 20, in main async for obj in pool: TypeError: 'async for' requires an object with __aiter__ method, got list So instead, checking the docs suggests using asyncio.gather for parallel execution of tasks, which takes a variable number of 'coros_or_futures'. On the first attempt, we saw that we had accidentally created a list of "coroutine objects", so lets go back and use that: --- asynctest.py.orig 2016-11-28 21:03:04.0 -0600 +++ asynctest.py 2016-11-28 21:03:35.0 -0600 @@ -16,9 +16,10 @@ async def main(): pool = [Counter(i) for i in range(5)] -results = [] +coros = [] for obj in pool: -results.append(await obj.count_down()) +coros.append(obj.count_down()) +results = asyncio.gather(*coros) return results loop = asyncio.get_event_loop() Output: <__main__.Counter object at 0x1026b6160> 4 starting <__main__.Counter object at 0x10213f978> 0 starting <__main__.Counter object at 0x1026b6128> 3 starting <__main__.Counter object at 0x1026af748> 1 starting <__main__.Counter object at 0x1026b60f0> 2 starting <_GatheringFuture pending> Now we've started everything asynchronously, but it exited way too fast and never completed anything. But instead of a list of results, we got a _GatheringFuture at the end of everything. So let's await that: results = await asyncio.gather(*coros) And now we get: <__main__.Counter object at 0x101eb6160> 4 starting <__main__.Counter object at 0x10063f978> 0 starting <__main__.Counter object at 0x101eb6128> 3 starting <__main__.Counter object at 0x101eaf748> 1 starting <__main__.Counter object at 0x101eb60f0> 2 starting <__main__.Counter object at 0x101eb6160> 4 completed <__main__.Counter object at 0x10063f978> 0 completed <__main__.Counter object at 0x101eb6128> 3 completed <__main__.Counter object at 0x101eaf748> 1 completed <__main__.Counter object at 0x101eb60f0> 2 completed [0, 1, 2, 3, 4] And there we have it. Our final script is: import asyncio class Counter: def __init__(self, i): self.count = 10 self.i = i async def count_down(self): print(self, self.i, "starting") while self.count > 0: # simulate a computation await asyncio.sleep(0.5) self.count -= 1 print(self, self.i, "completed") return self.i + self.count async def main(): pool = [Counter(i) for i in range(5)] coros = [] for obj in pool: coros.append(obj.count_down()) results = await asyncio.gather(*coros)
Re: Asyncio -- delayed calculation
On Tue, Nov 29, 2016 at 1:23 PM, Steve D'Apranowrote: > This is confusing: why is this awaiting something inside an async function? > Doesn't that mean that the await asyncio.gather(...) call is turned > blocking? "await" means "don't continue this function until that's done". It blocks the function until a non-blocking operation is done. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
Take a look at Doug Hellmann's example using multiprocessing at https://pymotw.com/2/multiprocessing/basics.html You should be able to substitute the count down example directly into the first example. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tue, 29 Nov 2016 12:03 am, Chris Angelico wrote: > On Mon, Nov 28, 2016 at 11:48 PM, Steve D'Aprano >wrote: >> When I try running that, I get no output. No error, no exception, the >> run_until_complete simply returns instantly. > > When I do, I get this warning: > > asynctest.py:17: RuntimeWarning: coroutine 'Counter.count_down' was > never awaited > obj.count_down() Ah yes, I saw that earlier, from an earlier version of the code that failed with an exception. But it was enough to raise the warning, which then was suppressed. > Putting an 'await' in front of that call causes the tasks to be run > consecutively, of course. The most similar code for running tasks > concurrently seems to be this: > > async def main(): > pool = [Counter() for i in range(5)] > await asyncio.gather(*(obj.count_down() for obj in pool)) > > Taken from: > https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks This is confusing: why is this awaiting something inside an async function? Doesn't that mean that the await asyncio.gather(...) call is turned blocking? -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tue, Nov 29, 2016 at 11:20 AM, Steve D'Apranowrote: > On Tue, 29 Nov 2016 02:53 am, Ian Kelly wrote: > >> In order for the coroutines to actually do anything, you need to >> schedule them in some way with the event loop. That could take the >> form of awaiting them from some other coroutine, or passing them >> directly to loop.run_until_complete or event_loop.create_task, or as >> Chris suggested awaiting them as an aggregate. > > I thought that's what I had done, by calling > > loop.run_until_complete(main()) That's invoking a single task, main(). If you were to write this code using threads, main would need to be thread-aware so that it can fork appropriately - it needs to start new threads for the subtasks. Tasks in asyncio are like cooperatively-switched threads or threadlets, and if you want them to run in parallel, you have to instruct them to run as separate tasks. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Tue, 29 Nov 2016 02:53 am, Ian Kelly wrote: > In order for the coroutines to actually do anything, you need to > schedule them in some way with the event loop. That could take the > form of awaiting them from some other coroutine, or passing them > directly to loop.run_until_complete or event_loop.create_task, or as > Chris suggested awaiting them as an aggregate. I thought that's what I had done, by calling loop.run_until_complete(main()) -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Mon, Nov 28, 2016 at 5:48 AM, Steve D'Apranowrote: > Let's pretend that the computation can be performed asynchronously, so that > I can have all five Counter objects counting down in parallel. I have this: > > > import asyncio > > class Counter: > def __init__(self): > self.count = 10 > async def count_down(self): > print(self, "starting") > while self.count > 0: > # simulate a computation > await asyncio.sleep(0.5) > self.count -= 1 > print(self, "completed") > > async def main(): > pool = [Counter() for i in range(5)] > for obj in pool: > obj.count_down() > > loop = asyncio.get_event_loop() > loop.run_until_complete(main()) > > > > > When I try running that, I get no output. No error, no exception, the > run_until_complete simply returns instantly. > > What am I doing wrong? Remember that coroutines are basically generators. Native "async def" coroutines are dressed up as something different, but they were still designed as a drop-in replacement for generator coroutines. If count_down were a generator function then simply calling it wouldn't really do anything and as a native coroutine it still doesn't (other than return a "coroutine object"). In order for the coroutines to actually do anything, you need to schedule them in some way with the event loop. That could take the form of awaiting them from some other coroutine, or passing them directly to loop.run_until_complete or event_loop.create_task, or as Chris suggested awaiting them as an aggregate. -- https://mail.python.org/mailman/listinfo/python-list
Re: Asyncio -- delayed calculation
On Mon, Nov 28, 2016 at 11:48 PM, Steve D'Apranowrote: > When I try running that, I get no output. No error, no exception, the > run_until_complete simply returns instantly. When I do, I get this warning: asynctest.py:17: RuntimeWarning: coroutine 'Counter.count_down' was never awaited obj.count_down() Putting an 'await' in front of that call causes the tasks to be run consecutively, of course. The most similar code for running tasks concurrently seems to be this: async def main(): pool = [Counter() for i in range(5)] await asyncio.gather(*(obj.count_down() for obj in pool)) Taken from: https://docs.python.org/3/library/asyncio-task.html#example-parallel-execution-of-tasks There may be other ways, but that's the best I could find. It seems to do what you want. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Asyncio -- delayed calculation
I'm a complete and utter newbie when it comes to asynchronous programming, so I may have the entire concept backwards here. But treat this as a learning exercise rather than something I'd really do. Suppose I have a bunch of calculations to do: count down from 10. So I have a bunch of objects: class Counter: def __init__(self): self.count = 10 def count_down(self): print(self, "starting") while self.count > 0: # simulate a computation time.sleep(0.5) self.count -= 1 print(self, "completed") pool = [Counter() for i in range(5)] for obj in pool: obj.count_down() Since that's all blocking, I wait for the first Counter to count down to zero before I move on to the second. Let's pretend that the computation can be performed asynchronously, so that I can have all five Counter objects counting down in parallel. I have this: import asyncio class Counter: def __init__(self): self.count = 10 async def count_down(self): print(self, "starting") while self.count > 0: # simulate a computation await asyncio.sleep(0.5) self.count -= 1 print(self, "completed") async def main(): pool = [Counter() for i in range(5)] for obj in pool: obj.count_down() loop = asyncio.get_event_loop() loop.run_until_complete(main()) When I try running that, I get no output. No error, no exception, the run_until_complete simply returns instantly. What am I doing wrong? What I expected is that the pool of Counter objects would be created, each one would have their count_down() method called without blocking, so I'd have something like: # IDs are simulated for ease of comprehension <__main__.Counter object at 0x0123> starting <__main__.Counter object at 0x0246> starting <__main__.Counter object at 0x048c> starting <__main__.Counter object at 0x0918> starting <__main__.Counter object at 0x1230> starting <__main__.Counter object at 0x0123> completed <__main__.Counter object at 0x0246> completed <__main__.Counter object at 0x048c> completed <__main__.Counter object at 0x0918> completed <__main__.Counter object at 0x1230> completed -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list