19 ms·
Go hits the concurrency nail on the head
- iainmerrick 8y agoMixing threads with event loops is possible, but so complicated that few programmers can afford the mental burden for their applications. This is just Apple's Grand Central Dispatch model, or the event loops used internally in Chromium. It's not complicated at all, it's a very practical and productive approach.
- vvanders 8y ago> Programming with threads is hard - it's hard to synchronize access to data structures without causing deadlocks; it's hard to reason about multiple threads accessing the same data, it's hard to choose the right locking granularity, etc. It's almost as if we need a language that has a focus on data races, concurrency and fearless threading.
- GFischer 8y agoRust evangelism strike force strikes again :) I thought of Erlang initially, and so did others in the thread.
- kazinator 8y agoActually it's very easy to avoid data-structure related deadlocks: - avoid holding two locks simultaneously: that guarantees no deadlocks. - if you have to hold several locks, always acquire them in the same order in all scenarios. The "avoid holding multiple locks" rule also harmonizes very well with "minimize the durations/sizes of critical regions". That is, hold a lock over the minimum number of machine instructions necessary. That reduces lock contention, promoting better concurrency. Of course, we don't get anything free and easy. Not holding multiple locks means that you can't lock in some consistent condition across multiple structures to do an atomic update. Any time you let go of a lock to go do something elsewhere and re-acquire the lock, the "world has changed". If the code hangs on to any previous assumptions about the state (e.g. cached info in local variables), there will be a bug.
- dreamcompiler 8y ago- Or if you have to hold several locks, grab them all atomically and if even one grab fails, release them all and try again later. - If you really need all the locks right now, steal the ones you don't own from another thread, and make sure all threads can deal with lock theft. It's not always easy to sort out the best way to deal with deadlocks. Each approach has significant tradeoffs.
- kazinator 8y agoMultiple locks are not always in the same scope. Method Foo::Update acquires a lock, then without releasing the lock calls into another class/object where Bar::Commit also acquires its own lock.
- dreamcompiler 8y agoYep. That's certainly one of the tradeoffs.
- pkaye 8y agoHow would you do this in Go? If not Go, what language has the capability?
- gatestone 8y ago"...shared values are passed around on channels and, in fact, never actively shared by separate threads of execution. Only one goroutine has access to the value at any given time. Data races cannot occur, by design." [...if you stick to this style of programming...]
- LukeShu 8y ago"avoid holding two locks simultaneously" only guarantees no deadlocks if locks are your only form of inter-thread-blocking. If you have synchronization points or channels/pipes or anything else, that needs to be expanded to include "avoid using a channel while you hold a lock" and similar for every blocking primitive.
- rubiquity 8y agoHah! Good one. To anyone else not in on the joke, parent is referring to the language that is often discussed on HN and known for having a sometimes overly enthusiastic community, Pony: https://www.ponylang.io/ https://www.ponylang.io/
- eatbitseveryday 8y agoHah! You must be in on another joke, because OP was referring to Rust ;) > Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety. www.rust-lang.org edit: /s
- nemothekid 8y agoWooosh Edit: I made this post, but I thought afterwards, what if you were you being dense on purpose, and I didn't get the joke?
- cube2222 8y agoI think this thread is a nice reminder that it's really helpful to use /s when you're using sarcasm in written form.
- deleted 8y ago[deleted]
- cpburns2009 8y agoIsn't Pony the language where x / 0 == 0?
- spooneybarger 8y agoYes but you can also have it be an error by doing x/?0 using the latest code on master.
- markdoubleyou 8y agoHah! Good one. Parent is clearly referring to Ada and the oft-quoted chapter 9 of NASA's Ada Style Guide (c. 1987). https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19870013758.pdf https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/198700... The discussion of pitfalls surrounding Ada's task facility is eerily familiar to what you're reading in this thread 31 years later.
- lurker_level_53 8y agoIs this a RUST reference?
- pknopf 8y agoYes
- deleted 8y ago[deleted]
- reacharavindh 8y agoI thought this was one of the "not strong" points of Rust? It had Tokyo, futures, async/await but I vaguely remember many libraries waiting for the async story to settle down? Is there a one standard way to do async now in Rust stable?
- steveklabnik 8y agoYou can use tokio with stable Rust. Async/await is in nightly, but won’t be stable until early next year. As part of that work, some of the APIs are changing, but there is/will be a compatibility layer to make upgrading from the stuff today to tomorrow’s stuff easier. That said, to address the question directly, rust-the-language provides a stronger guarantee than Go: rust code is free of data races at compile time. However, the APIs are much harder. Async/await will make them much easier, but still not as easy as “everything is always async.” The tradeoff is speed and safety; rust will be faster and have more guarantees, but be slightly harder to use (though some people do think the explicitness of async/await is easier, but that’s personal opinion).
- reacharavindh 8y agoThanks for the patient and detailed response Steve. I don't have a particular need for Async at the moment, and commented impulsively, but ended up learning more about current state of affairs from your response :-)
- 8y ago
- seanmcdirmid 8y agoJefferson’s Timewarp did that with “virtual time” using optimistic rather than pessimistic concurrency constructs so popular today outside of transactions.
- sitkack 8y agoHave they moved on from Starship?
- seanmcdirmid 8y agoNo connection with the band.
- flavio81 8y ago>It's almost as if we need a language that has a focus on data races, concurrency and fearless threading. How Exciting!! How Exciting!!
- tick_tock_tick 8y agoWhile I really do like Erlang I think everyone should take a look at what Elixir is doing on top of the Erlang VM. https://elixir-lang.org/ https://elixir-lang.org/
- deleted 8y ago[deleted]
- deleted 8y ago[deleted]
- erokar 8y agoAccording to the author Go make concurrent programming "the best experience, by far, compared to other popular programming languages today." I beg to differ. I fail to see why I should choose Go over Elixir/Erlang for concurrency. Elixir's cuncurrency mechanisms are at least as good — and I would argue better — than Go's, and Elixir as a language has an expressiveness that Go lacks.
- vvanders 8y agoI think the main argument is that Elixir isn't as mainstream which is a fair point. That said I agree with everything, Erlang pioneered in this space and has shown to scale very well[1] in a proven way over the last few decades. [1] https://phoenixframework.org/blog/the-road-to-2-million-websocket-connections https://phoenixframework.org/blog/the-road-to-2-million-webs...
- mirceal 8y agoYes. Because mainstream is what you should select for when choosing your tools. On a more serious note: I’m very wary of people that have discovered the one true language, framework, etc. That’s how you end up with 100 lines of go instead a one line bash script. That’s how you end up with “java developers” that are more concerned with design patterns instead of actual working code. I could go on. Learn about as many things possible, form your own opinions, choose the right tool for the job.
- hmschreck 8y agoWe had a developer interview here who was an Elixir zealot - and I use that word entirely purposefully. He was very adamant that we needed to rewrite our entire platform in Elixir because it was obviously better. We ended up not hiring; he refused to touch certain technologies that make up a core part of our stack (Node being the biggest issue), and he was honestly something of a massive tool -- on our take-home thing (takes most developers maybe a few hours) he limited himself to one hour, didn't get it done, wasn't even solving the right problem, and, when he sent in his solution... "It's ok, I know you won't get it, but that's OK."
- GatoVolador 8y agoPHP is way better with this than Go, and much easier to learn.
- cube2222 8y agoCould you please expand how it's better in your opinion?
- vthallam 8y agoI think you are being down-voted for just making a generalized claim with no mention of why? care to explain, what areas does PHP out-perform Go?
- TheDong 8y agoPHP's concurrency model is really nice: Spawn a process per request, and otherwise don't do concurrency because that's a mistake. Yes, you can use CGI to get the same model in practically any language, but we see very few languages these days making this tradeoff. For some reason, people decided they wanted the performance gain of spawning threads per request instead of processes per request. Honestly, it's ridiculous. Reasoning about isolated processes per request is easy compared to many threads. Sure, in php you can't share a pool of db-connections between requests, and if you need to lock a filesystem resource you're stuck because you can't share a mutex between requests so you have to make an ad-hoc one some other way, and the overhead is like 50MB/request meaning the difference between a server handling 1000 requests with php and 60000 requests with go... But being able to just have processes which were all isolated and request scoped was nice. The OS was your GC so you didn't have to worry about freeing memory, and the OS's scheduler did a damn good job of making sure php processes blocked on IO got context switched between.
- jrs95 8y agoPHP no longer works like this in practice. FastCGI etc uses persistent processes with a thread per request, and thread per request is pretty standard for most platforms that don't have good async support at this point.
- thinkpad20 8y agoI think the author might be overstating how unique Go’s position is in terms of making concurrency easier. Haskell has the best concurrency story of any language I’ve used. Super lightweight green threads, simple concurrency primitives like MVar and STM, good performance (possibly requiring tweaks, but not bad out of the box). Referential transparency (by which I basically mean immutable data) makes sharing data across threads much easier and in some cases more performant by allowing you to avoid copying. Plus, you have a type system which makes it much easier to reason about your code. All that being said, I haven’t written Go and can’t compare the two. Also, Haskell doesn’t support the actor/message passing model out of the box (although libraries exist for it) or prevent deadlocks (although the immutability helps a great deal here). BEAM languages, clojure, rust, pony and others all have their strengths here — again, this doesn’t discredit the article at all but the idea that Go is the clear winner is debatable.
- jerf 8y agoIn terms of raw capability, Haskell is the concurrency winner. Not only does it have basically every paradigm, they even all work together reasonably well. (Not perfectly, but reasonably well.) But I don't think you can argue Haskell is mainstream. It continues to be on a fairly slow growth trajectory from what I see, but it's not in any danger of cracking into the top tier language set anytime soon. Go just might in another 5 years.
- thinkpad20 8y agoTrue, but I think that the "mainstream-ness" of Go (debatable as it is) was of secondary importance to the article, which didn't even mention that there are other languages out there which solve the concurrency problem in other/better ways. Whether or not a language is mainstream is more or less a matter of opinion, in any case. Certainly Haskell, Rust or Erlang (to name a few) are probably less widely used than Go, but none of them are obscure, so to not mention them at all suggests that the author is either being disingenuous, is not aware of those languages' capabilities, or simply forgot.
- dnautics 8y agoI think the BEAM languages "pure functions... well except for message passing" is perfect for concurrency. With haskell you have to use monads which are less convenient. And idiomatic BEAM defaults to actor model if you consider OTP to be "out of the box (you should)". Finally, pattern matching function guards are incredibly useful for parsing incoming messages that might be polymorphic. Haskell is kind of designed to make programming as pure as possible and the BEAM was designed with concurrency as the first priority; it's "pure functional" to the point where its advantages (referential transparency) help concurrency and the impurities are the precise set of compromises you need to make concurrency easy.
- da02 8y agoWhat other languages/platforms are out there that are as good with concurrency as Go?
- ken 8y ago> I'm happy to go on record claiming that Go is the mainstream language that gets this really right. And it does so by relying on two key principles in its core design... The unmentioned third principle that it relies on is: "Curly braces, so it looks almost like C if you squint". That's what makes a language "mainstream" these days. It looks like Go is very good at concurrency, but from everything I've read, I don't see how it's any better than Erlang or Clojure. The only controversial part of Eli's claim is the implication that other languages that get concurrency right aren't "mainstream". That's not a well-defined term and so naturally this is going to irk many people. Perhaps the title would have been more accurate as "Go hits the concurrency nail on the head, using the hammer of K&R style". :-)
- the_clarence 8y agoYou’re mentioning functional languages. That’s not for everyone or every usecase. (I love Erlang.)
- ken 8y agoSo, in the world of concurrency, are imperative languages good for every person and every use case? :-) My intention was not to single out functional languages. Those were just the first languages I thought of with the best concurrency support. (I don't think it's mere coincidence, though, that so many concurrency-aware languages are functional. If concurrency is a major concern, perhaps one should consider that "mainstream" or even "imperative" might not necessarily be a hard requirement.) How about Occam or Crystal, then, which use CSP and are imperative yet don't have curly braces?
- AnimalMuppet 8y ago> So, in the world of concurrency, are imperative languages good for every person and every use case? :-) Clearly, no. I think it comes down to the question of shared mutable state. Functional languages say "don't do that, it's evil" - with some justification. If the problem doesn't push you toward shared mutable state, then consider functional languages. When could the problem push you toward shared mutable state? I worked on a video router. You had maybe 100 video sources, maybe 80 video destinations, and six different sources of control. All six sources of control needed to see the same state of what inputs were connected to what outputs. So the fundamental nature of the problem was one giant shared mutable state.
- tracker1 8y agoRegarding one of the last comments, I'd say the two biggest reasons for using Node over Go, at least initially. Prototyping speed, and a stronger connection to a web front-end. I've really not seen any other language/platform work faster for developing a huge variety of implementation details than JS/Node. IT's a really good balance of performance, flexibility and ease of development. Is it a Panacea? Of course not. That said, I think that starting more monolithic an breaking pieces off as needed is a strong approach, best started with Node. From there, you want different pipelines/processes/queues/workers in other platforms, great. Write that piece in go+grpc and call it from your api endpoint server. So many times I see devs want to go the optimal X, without even considering if "optimal" is needed, and if it's prudent to start with.
- tracker1 8y agoAlso, can always front with gopher (written in go) and defer to other systems via reverse proxy.
- fixermark 8y agoAnd the main reason to not use Node: JavaScript.
- deleted 8y ago[deleted]
- anothergoogler 8y agoWhat a lovely fusion of the blub paradox and a middlebrow dismissal.
- tracker1 8y agoKind of, though your tone seems a bit dismissive. In the end, the Node/JS community, mostly because of npm, has a lot to offer and that is the ability to create a working product more quickly than most other options in most conditions.
- dreamcompiler 8y agoOr you could just use the Actor model, which I like much better than CSP. (Or at least I think I'll like it better when I finally understand it.)
- sagichmal 8y agoThe Actor model is trivially implementable with CSP.
- notriddle 8y agoAnd CSP is trivially implementable using the Actor model. What's your point?
- Matthias247 8y agoDepends. I would argue that e.g. Go's CSP approach is not implementable on top of Akka, since actors there are not allowed to block on reception of single message. They will always get messages pushed, so the blocking Go concurrency constructs can not be built on top of it. It's obviously slightly different with Erlang, where Actors can block. I think the basic Actor definition does only say that Actors need to send messages to others, but not whether they can block on reception of selected responses. So it might be a bit undefined.
- zzzcpan 8y agoLack of blocking support in runtime cannot prevent you from implementing waiting. Either through higher-order event driven code or just plain busy-waiting style message exchanging until you receive your message.
- Matthias247 8y agoBusy waiting won't work, since there will potentially never run another thread that changes the thing you are waiting for (that's why e.g. in JS everything needs to be async). There might be ways to model things a bit different (like using Promises and other monadic abstractions), but things will never look the same as in a blocking environment.
- dmead 8y agoThis picture is a bit rosey, no? Literally the only deadlock I've come across in years has been in go.
- gjm11 8y agoWhat fraction of the concurrent programming you've done over those years has been in Go?
- tejasmanohar 8y agoSame question. I've come across deadlocks in popular databases for heck sake haha.
- apta 8y agoA language claiming to be designed to solve the concurrency issue needs to have a better story than what golang offers.
- dmead 8y agohalfish? the other half being akka.
- bunderbunder 8y ago> Programming with threads is hard - it's hard to synchronize access to data structures without causing deadlocks; it's hard to reason about multiple threads accessing the same data, it's hard to choose the right locking granularity, etc. That's a list of problems that are specific to mutable state that is shared among threads. As the old saying goes, "If it hurts, don't do it." We've had ways of doing multithreaded code that are easier to reason about for decades. They really do work quite well. Why people doggedly insist on pretending they don't exist is a perennial mystery to me. Even if your programming language wasn't kind enough to include a better concurrency model in its standard library, there are always third-party libraries. I realize my experience isn't universal, but, personally, I've discovered that there's precisely one scenario where I ever need to resort to code that involves mutexes: When the business requirements and the performance profiler have conspired to kidnap my children and hold them for ransom.
- Izkata 8y ago> We've had ways of doing multithreaded code that are easier to reason about for decades. They really do work quite well. Why people doggedly insist on pretending they don't exist is a perennial mystery to me. Not a single one was mentioned during my CS undergrad; shared-memory threads were, many times. I think simple ignorance is the answer.
- bunderbunder 8y agoI think a simple love of complexity is also part of it. It's fun to do things that make you feel clever. One experience I've had more often than I'd care to admit is diving into a multithreaded module with the intent of fixing a race condition bug, and finding that I could simultaneously remove the bug and realize a healthy performance improvement by making it single-threaded. Which, for that matter, is another reason to be wary of mutexes and shared mutable data: Memory barriers do really impolite things to pipelines and caches in a modern CPU.
- siscia 8y agoI don't believe this holds. We are suppose to be engineers, we need to do our research on design and methodologies before to code. And even if we really don't find simpler solutions, there is supposed to be a more senior engineer check our code and provide feedback. Ignorance should not be an excuse...
- erik_seaberg 8y ago> Proper use of channels removes the need for more explicit locking If you're lucky. Sharing mutable state is unsafe by default (map writes can crash!) yet very common and the language doesn't help you avoid it. A good language for concurrency would also make it easy to switch between sync and async method calls; the trouble with channels is they don't support passing errors and panics without rewriting everything to wrap them in a pseudo-generic struct.
- camgunz 8y agoGo's answer to this is tooling, -race in this case. I think Go's general strategy of moving complexity/functionality to external tools is a little underexamined -- it's definitely interesting, especially as it's kind of a middle path between "language with lots of helper stuff" and "IDE with lots of helper stuff".
- erik_seaberg 8y agoIt's better than nothing, but it's so expensive they don't really expect anyone to use it at prod scale. That's a recipe for letting catastrophic bugs do damage for hours or days.
- weberc2 8y agoIf you're using channels "properly", then you only have one thread writing to the map at a time. In this case, "proper" use of channels is easy enough, but there are plenty of cases where "proper" use of channels is (in my opinion) a fair bit harder than proper use of mutexes, which is one of the problems Go's concurrency scheme was meant to solve. I will say that even in spite of the above criticism, I've found that writing safe, concurrent Go is quite a lot easier (channels are still useful if not the panacea they were made out to be), but it still takes a bit of planning to minimize the risk of creating race conditions (minimizing the interaction points between goroutines, not throwing goroutines at every problem, etc). This takes experience which requires social controls instead of technical controls, which is a bummer, but on balance still better (IMHO) than the problems brought by other languages with technical controls for this particular problem.
- siscia 8y agoLeave aside the extremely interesting engineer behind th go scheduler and goroutine. The abstraction that goroutine provides is a simple, indipendent, isolated unit of execution. You start it, and that is all you can do with it. No way to set priorities, decided when to stop it or inspect it. After the goroutine start the only interface that you get is a channel where to push and pop stuff from. Which is just too limited consider the complete lack of genetics. It is really the best we can come up with?
- apta 8y agoI'm not sure how people can't see that futures (as in Java's CompletableFuture) is much superior to golang's approach exactly due to the reasons you mention. You can also have the actor approach (e.g. Akka) to set priorities, have monitoring hierarchies, and introspection.
- favorited 8y ago> Mixing threads with event loops is possible, but so complicated that few programmers can afford the mental burden for their applications. Correct me if I'm wrong, but doesn't basically every UI framework from the last 20 years do exactly this?
- Matthias247 8y agoThey do, but I think also that possibly every user of such a framework has at least once created a concurrency issue once in their life. All of these the issues below are things I have seen (and created) over and over in these environments: - Trying to access UI framework elements from the wrong thread - not understanding that a callback inside one of the included libraries is not running on the UI thread but from somewhere else - callbacks capturing references to objects which already had been destroyed (but the callback had been queued and can't be cancelled), and accessing those objects later on - deadlocks, due to mutex locking in both callbacks as well as the "normal" code. There are ways to minimize the amount of issues, e.g. always deferring asynchronous callbacks to the next eventloop iteration, making sure that callbacks are queued on the main thread instead of an arbitrary one, deferring object deletion behind all other callbacks, etc. But these are on the harder to learn, teach and enforce side. Therefore I would agree with the author of that article that the mixture of eventloop-based programming and multiple threads is the hardest combination.
- tybit 8y agoC# does this server side too, async isn’t particular fun but threading doesn’t make it any worse when the runtime handles it imo.
- zzzcpan 8y ago> Correct me if I'm wrong, but doesn't basically every UI framework from the last 20 years do exactly this? Yeah, multithreading with event loops was never a big deal. Simple API to run something in a thread pool out of event loop is definitely much easier, than using threads. Apple did Grand Central Dispatch and I believe it's universally recognized to be easier than using threads.
- kasey_junk 8y agoIts fairly interesting that this article doesn't mention actors, futures/promises and async/await style co-routines which are all extremely available in all of the major languages available today and broadly used (with the possible exception of golang). Frankly, I think the concurrency story is one of the weaknesses of golang. Contrary to what this article says you cannot stop thinking about concurrency in your code in golang like you can in some of the more avante garde concurrency models (STM) and it doesn't provide nearly the sophistication in type or post build checking of other languages.
- tschellenbach 8y agoFor most use cases of async programming you never need to touch channels and goroutines. If you build a webserver in go it will be highly concurrent out of the box. as a developer you just write synchronous code. here's a nice tutorial: https://getstream.io/blog/go-1-11-rocket-tutorial/ https://getstream.io/blog/go-1-11-rocket-tutorial/
- HeyImAlex 8y agoJust my own anecdote, but I write go and typescript daily and I’d take goroutines and go’s other concurrency primitives over aysync/await any day of the week.
- lostmsu 8y agoWhat is the difference?
- jy3 8y agoDescribing its concurrency model as one of its weakness is a bold statement, because it's probably one of the biggest reason it became widely adopted. Everyone is different, but Go 's concurrency model has been the easiest for me to reason about. And I've dealt with my fair share of "async/await".
- kasey_junk 8y agoMaybe. But I’ve been developing golang full time in high scale concurrency environments for 4 years and working with a team of similar people. It’s an opinion that is near universally shared on that team. At high concurrency levels almost everything abandons standard golang concurrency patterns and tools.
- pcwalton 8y agoGo concurrency is just threads. It's a particularly idiosyncratic userland implementation of them. There are two claims here I'd like to unpack further: 1. "I've measured goroutine switching time to be ~170 ns on my machine, 10x faster than thread switching time." This is because of the lack of switchto support in the Linux kernel, not because of any fundamental difference between threads and goroutines. A Google engineer had a patch [1] that unfortunately never landed to add this support in 2013. Windows already has this functionality, via UMS. I would like to see Linux push further on this, because kernel support seems like the right way to improve context switching performance. 2. "Goroutines also have small stacks that can grow at run-time (something thread stacks cannot do)." This is a frequent myth. Thread stacks can do this too, with appropriate runtime support: after all, if they couldn't, then Go couldn't implement stack growth, since Go's runtime is built in userland on top of kernel threads. Stack growth is a feature of the garbage collection infrastructure, not of the concurrency support. You could have stack growth in a 1:1 thread system as well, as long as that system kept the information needed to relocate pointers into the stack. Goroutines are threads. So the idea the "Go has eliminated the distinction between synchronous and asynchronous code" is only vacuously true, because in Go, everything is synchronous. Finally, Go doesn't do anything to prevent data races, which are the biggest problem facing concurrent code. It actually makes data races easier than in languages like C++, because it has no concept of const, even as a lint. Race detectors have long existed in C++ as well, at least as far back as Helgrind. [1]: https://blog.linuxplumbersconf.org/2013/ocw/system/presentations/1653/original/LPC%20-%20User%20Threading.pdf https://blog.linuxplumbersconf.org/2013/ocw/system/presentat...
- roganartu 8y ago> Finally, Go doesn't do anything to prevent data races, which are the biggest problem facing concurrent code. It actually makes data races easier than in languages like C++, because it has no concept of const, even as a lint. Race detectors have long existed in C++ as well, at least as far back as Helgrind. While it can't guarantee correctness outside of runtime (and even then, obviously only if whatever you are running actually triggers the race), a race detector has been part of the Go core since 2012[1]. [1]: https://blog.golang.org/race-detector https://blog.golang.org/race-detector
- rustcharm 8y agoI disagree. You really need "shared nothing" and messages. The only language that really hits the concurrency nail on the head is Erlang, which we use extensively in our business.
- truth_seeker 8y agoErlang is latency sensitive and Go is throughput sensitive
- spinningslate 8y agoDon't disagree with the challenges about other languages being equally applicable. My first thought was also "Erlang does it at least as well as Go". Reasonable challenges on whether Erlang (or Elixir/Pony/...) are mainstream though. For me the more valuable point is not that Go specifically gets it right: it's that async - as implemented in javascript/python/java/c# and so on - is fundamentally wrong. These two quotes get to the heart of it: >The core idea is that in the asynchronous model we have to mentally note the blocking nature of every function, and this affects where we can call it from. >The fundamental issue here is that both Python and C++ try to solve this problem on a library level, when it really needs a language runtime solution. I've said for a while that async as implemented in javascript et al is the "GOTO" of concurrency - and should be considered equally as harmful as Dijkstra's observation on GOTO, for many of the same reasons [0]. [0] https://en.wikipedia.org/wiki/Considered_harmful https://en.wikipedia.org/wiki/Considered_harmful
- notriddle 8y agoShared-memory threads are worse than async/await. I'd rather have GOTO than data races, since, while it might be hard to model in your head, at least it's deterministic and using it wrong doesn't cause UB. p.s. https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ https://vorpus.org/blog/notes-on-structured-concurrency-or-g...
- spinningslate 8y agoErlang doesn't do shared memory. It's a message passing, shared-nothing model. I believe GO is the same.
- tomjakubowski 8y agoGo is not shared-nothing: if you write a slice to a channel between two goroutines, for example, the memory that the slice references is now shared between the goroutines
- 8y ago
- accnumnplus1 8y agoFor me, "great tools like the race detector" sums up the article - the detector is fallible, and although every point in the article has some validity, they also could all be argued against, and the result is a bit of a house of cards. So for me, Go at work by order, Erlang at home by choice.
- iainmerrick 8y agoDo people really find CSP a good approach to concurrency? I've always thought that channels were a relatively poor choice of fundamental primitive -- channel-based concurrency is tricky to get right and not very composable. Most viable concurrency primitives are in some sense equivalent (you can build condition variables out of channels and vice-versa, say) but that doesn't mean they're equally good. Java has per-object "synchronized" and "notify" as its core constructs, but it's a bad idea to use those directly for day-to-day concurrency tasks. Much better to use library classes like ThreadPoolExecutor and Future. In Go, do you tend to use channels directly, or do you use higher-level wrappers?
- apta 8y agoExactly. I get the feeling that people who claim golang gets concurrency right have not used the much more powerful constructs in the Java standard library. They keep getting better with things like CompletableFuture which was added in Java 8, and coroutines which are going to be added to the JVM sometime down the line by means of project Loom. Composability in golang concurrency constructs is non-existent. Having to manually manage channels to indicate errors, return values, and completion is error prone and does not compose, and is subject to race conditions. I don't see anything that I can do in golang that I wouldn't be able to do using Java's futures, while still being more composable and easier to reason about in Java.
- int_19h 8y agoI think composability especially is the key point. With futures (and async/await sugar on top), everything is naturally composable. It's much trickier with channels.
- kahlonel 8y agoApparently there is a C library for green (Go-like) threads called "libdill" or "libmill".