19 ms·
A lot of people seem to have trouble wrapping their heads around async programming and I'm not sure if this article will help that much. What are the most conf
by orf 10y ago
A lot of people seem to have trouble wrapping their heads around async programming and I'm not sure if this article will help that much.
What are the most confusing aspects of using async/await? Is it the implementation (i.e the syntax in JS, C# or Python) or the theory behind it?
- bullen 10y agoI think it's because async. means nothing if you don't specify how, where and when something is async. For starters you can't use languages that don't share memory between threads if you wish to see any improvements by adding async. threading to your non-blocking network code. So Python, Go, Ruby, Javascript, Erlang are all out of the argument when talking about async. Only C (C++, etc.) and Java (C#, etc.) families of languages that share memory between threads can scale better with async. on multicore processors when it comes to network related performance. Edit: Please comment if you downvote. Edit: Now I can't comment, the site tells me I'm writing too fast.
- kjksf 10y agoFYI: Go, Python and Ruby do share memory between threads. JavaScript doesn't have threads so the point is moot. Only Erlang doesn't share memory between processes.
- bullen 10y agoOk, didn't know that, can you point to some pages that explain how you can reference the same memory from two threads in the same process with these languages?
- kornish 10y agoHere's a snippet of Go code, with comments, which spins up different goroutines (lightweight thread primitives) to read and write values in a shared map: https://gobyexample.com/mutexes https://gobyexample.com/mutexes
- bullen 10y agoCan goroutines run on separate cores? Because I'm pretty sure go explicitly decided to not share memory between threads because google thinks async. is too hard to understand for programmers...
- geoka9 10y agoYou probably mean channels - yes they let you avoid dealing with synchronization of shared memory access explicitly, but they still use mutexes underneath, so you can use those directly if you want (instead of/along with channels).
- fzzzy 10y agoYes, goroutines run on separate cores. Go uses an M:N threading model. goroutines (M) which are ready to run are scheduled on a thread pool of POSIX threads (N) which I believe is sized to match the number of cores in the processor. Nothing stops those goroutines from racing on shared memory. However, there is a convention that before accessing some shared memory, goroutines synchronize themselves using channels. ("Do not communicate by sharing memory; instead, share memory by communicating.")
- kevin_thibedeau 10y ago> Only Erlang doesn't share memory between processes. Nim doesn't share memory either.
- KirinDave 10y agoAre you sure? I thought nim could use shared memory semantics if you wanted to? My understanding was it (cleverly) makes a subset of the language for concurrent work that requires you to be explicit about that activity.
- petre 10y agoYou can do async/event based programming using coroutines instead of threads, thus achieving non blocking behaviour. Nginx does that for instance.
- bullen 10y agoSure, but then you are not having all cores contributing to the same solution. Each nginx request is then distinct from every other request. And really then it's easier to scale with separate machines like google does in it's completely synchronous environment.
- petre 10y agoSo you start a number of workers and distribute the load among them. At least that's one way to solve the problem in Perl/Python/Ruby. You can contribute to the same solution by passing data to a master process, or using shared memory. In Erlang and Scala you can solve it even more ellegantly using actors and chanels. Go has goroutines and channels. All of these can span multiple cores.
- KirinDave 10y agoImportant point: Promises, ContT monadic constructions[1], Threads, and Actors all are just variations on how to structure sequential computation with asynchronous events. You can prove this to yourself via construction: you can write an interpreter that simulates threads (and even pre-emption) with Promises, Actors, or a more formal monadic style. You can repeat this feat for any single member of the set and if you do it carefully that's a proof by construction. Related reading, somewhat famous: https://pdfs.semanticscholar.org/2948/a0d014852ba47dd115fcc70202c840d71cac.pdf https://pdfs.semanticscholar.org/2948/a0d014852ba47dd115fcc7... Essentially what you choose to implement is arbitrary and should be informed by your underlying performance requirements coupled with your preferences. And, as people have noted: your post seems confused. Everyone benefits somewhat uniformly from asynchronous operations. Java's implementations are often a little bit more tunable because the programmer can provide the threadpool for async workers. Of all of the languages you named, Erlang is the most distinct as it offers an abstraction (and cost) of 1 heap per actor from the perspective of the garbage collector. This decision (made in a time when GC algorithms were quite a bit worse than they evolved to be) lead Erlang to something they want to keep: an abstraction that makes remote and local computation identical. Other languages that follow in Erlang's footsteps (Pony comes to mind, yay Pony!) do not do things this way. [1]: Okay EitherT ContT. Sure. Get picky. P.S., You cannot comment because your overall karma is too low, putting you in a rate-limited category. It generally clears up in a few hours. However, I do not think there is much more of value to say other than to thank that one poster for a few pretty cool papers!
- bullen 10y agoI have written my own async. webserver and on top of that my own distributed database: https://github.com/tinspin/rupy https://github.com/tinspin/rupy Here you can read about how async. works in my system: https://github.com/tinspin/rupy/wiki/Fuse https://github.com/tinspin/rupy/wiki/Fuse I might not be stroking the HN community in the right direction but trust me, I'm not confused.
- KirinDave 10y agoWould you prefer I use the phrase "completely incorrect about the memory models of the majority of languages you named and the erroneous conclusions you reached from them?" That is certainly more accurate, but seemed needlessly confrontational in my first draft, so I opted for the more generic phrase "confused post", which implied maybe the post itself was simply jumbled up and didn't clearly offer your intent to the audience. You did ask after all. I was trying to give a non-confrontational way to explain why I almost downvoted your post for being very misleading, and opted instead to respond with a slightly more correct post. HN needs less fighting and more knowledge. I thought what you were trying to say was, "parallelism is not the same as concurrency" but that it got jumbled up. That is a correct statement.
- deleted 10y ago[deleted]
- red2awn 10y agoI get the theory behind it and the syntax/API is fairly straight forward, but I just doesn't seem to be able to write async code :(
- KirinDave 10y agoTry writing code with JavaScript promises in the browser or on NodeJS. There is a ton of literature and work there to bring people on board. C# has some good docs too.
- throwasehasdwi 10y agoI don't think it confuses anyone that's worked on lower levels, particularly listening on sockets or kernel callbacks. It's a new concept to a lot of upper level programmers that used to be isolated from this stuff by threads.
- deleted 10y ago[deleted]
- unethical_ban 10y agoAs someone who has NOT gone low level into the theory of programming languages (bailed out on CS a decade ago), I wonder what the practical difference is between this and threading or multiprocessing.dummy. I understand the idea of blocking I/O and skipping that until a function/thread returns.
- charles-salvia 10y agoThe practical difference is that with async I/O, there's less overhead because you don't need a separate thread for each blocking I/O operation. It's the difference between using a single-threaded event loop to demultiplex between 10,000 sockets, and using 10,000 threads to read one socket each. The former strategy has way less overhead. Of course, many high-performance, I/O intensive applications like web-servers can effectively make use of a combination of multiple threads and I/O demultiplexing as well.
- deleted 10y ago[deleted]
- KirinDave 10y ago> I understand the idea of blocking I/O and skipping that until a function/thread returns. You understand it better than you realize. The idea of a "thread" is that it's a sequentially run context for computation with a bunch of context carried with it (a stack, references to a heap). This sequence of computations can be pre-empted. When OO systems offer "promises" what they're doing is essentially freeing a thread from the continuity of the computation and leveraging closures or object scope to hold the context. Instead of a thread representing the computation, now threads pick up computations and work on them until they stop for I/O, then resume them when the underlying async I/O layer has work for them. I can tell you about it in terms of monadic computations or how it relates to other concurrency strategies if you'd like, but it will become more theory-dense rather quickly.
- zzzcpan 10y ago"what the practical difference is between this and threading" The biggest practical difference is synchronization-free programming. In other words event loops are already always synchronized and you can do whatever you want with shared memory knowing that no other code is going to interfere until you explicitly stop and give up control. This allows for more reliable and more complex concurrent applications with less effort.
- amyjess 10y agoFor me, I didn't understand async/await until I saw it in Python. The C# explanations never worked for me, and it wasn't until after I saw the Python version that I realized why: C# uses an implicit thread pool so you can run async functions directly. With Python, you have to manually add your awaitables to an event loop and then start the loop, but C# just magicks all the glue into place. This is one of the places where the Zen of Python comes to mind: > Explicit is better than implicit. Edit: To see what I mean, compare Microsoft's async/await example to Python's: https://msdn.microsoft.com/en-us/library/mt674882.aspx https://msdn.microsoft.com/en-us/library/mt674882.aspx https://docs.python.org/3/library/asyncio-dev.html https://docs.python.org/3/library/asyncio-dev.html The Python example tells you how to set up an event loop and add awaitables to it, while Microsoft's "complete example" just has you add async event handlers to a window without showing you anything about setting up an event loop. It wasn't until I saw the event loop being set up in Python that I finally understood what async/await did.
- aprimc 10y agoThe most confusing thing in Python is that there is more than one way to do it. :) Python asyncio library supports coroutines, callbacks, futures, thread-pool executors... All have similar APIs and dissimilar semantics. For instance, canceling a coroutine is different than canceling a future and different than canceling a thread. (I've heard asynchronous exceptions are bad, but this is just an example.) Error handling is difficult. You can't observe an error if you don't remember to write code to observe an error. Also confusing is that coroutines in Python can be driven by any event loop. Which is great! You can write your own. But that will be even more confusing. Erlang has one OTP, Python will have many.
- Animats 10y agoThe most confusing thing in Python is that there is more than one way to do it. :) Exactly. Python had a multiple thread and blocking I/O model. That's well understood. Javascript had a single thread callback-based model. That's well understood. When you have both concepts in the same system, things get much more complicated. It's even worse when it's a retrofit. Consider Python's Queue module. If you call Queue.get(), your thread can block. But the async system doesn't understand that kind of block. (Is that right?) So you've blocked all the async stuff running off that thread. Go has lightweight threads too, called goroutines. But because it wasn't a retrofit, all lock mechanisms are compatible with lightweight threads. When a goroutine blocks on a lock, its underlying thread is re-dispatched. Python 3.6 isn't set up that way, because async operations were retrofitted in parallel with the existing machinery.