33 ms·
Concurrency I can finally understand and write correctly
- jonalmeida 9y agoIf anyone is looking for another language that also uses the actor model for concurrency and has very similar ways of handling it like D, then checkout Erlang. It's language lessons are clearly still relevant today. :)
- athenot 9y agoI'm a big fan of Erlang—and believe it's a useful language to know even if the daily language is something else—but my understanding of D is that it excels in a different problem space (build efficient native code, etc.)
- elsurudo 9y agoImportant to point out Elixir as well, which is touted as a "more modern" way to write code that runs on the Erlang VM (so you get the same concurrency features, etc.)
- strkek 9y agoThe _only_ downside I find of Elixir is that it looks too Ruby-ish to my taste. I'm still waiting for the day a C-like language that runs on BEAM becomes mainstream enough.
- StavrosK 9y agoHere's my thought process while reading your comment, which illustrates why we can't have nice things: > The _only_ downside I find of Elixir is that it looks too Ruby-ish to my taste. Yeah, preach it! > I'm still waiting for the day a C-like language that runs on BEAM becomes mainstream enough. Eww.
- strkek 9y agoHaha yeah, I've become too used to C-like syntax (C, Go, Rust, etc). I wouldn't mind a Lisp syntax too, but Ruby and ML are definitely a "no" from me. I just can't bring myself to like them. Their syntax feels too "loose", not sure if that makes sense. Btw, I know there are some Lisp-like implementations out there, but same criteria applies: still not mainstream enough as of now.
- StavrosK 9y agoNo, I agree with you completely, I'm just remarking on how everyone will have different aesthetics and things they're used to, and, ultimately, that might not be a valid complaint. Like, sure, I don't like Ruby's syntax much, but if the ecosystem is good and the language is a joy to write, eh, I'm going to compromise because you can't please everyone's subjective preferences.
- krick 9y agoCould you clarify, what's wrong with the Ruby syntax, exactly? I don't think there's a single programming language I like in terms of the syntax, but I don't see anything really wrong with Ruby. Surely not to the point I wouldn't use otherwise nice language because of it. Is python a no-no as well?
- heavenlyblue 9y agoHe thinks the syntax doesn’t “pop”, clearly.
- strkek 9y agoIt's not "wrong"; it just doesn't appeal me. - `elsif` - `end` - `unless` - `a unless b` - `f 1` vs `f(1)` - Implicit method calls (`f` vs `self.f`). I know both have different behavior, I just don't like this "implicit self". ... etc. I find Python okay-ish though, as paradoxical and nonsensical as that might sound. But in my toolchain Python has been reduced to little more than a calculator, since it's been replaced by Go for most of my automation needs. Btw, friendly reminder that this is my subjective opinion regarding my own tastes, not absolute truth on aesthetics. EDIT: Formatting.
- ancarda 9y ago>I'm still waiting for the day a C-like language that runs on BEAM I used to have a similar desire! I actually started working on a language that transpiled to Haskell, like CoffeeScript -> JS. What I found, however, is a lot of little ways to make it more and more terse, until I ended up with something resembling Haskell’s syntax. After this happened several times, I had some new found appreciation for Haskell’s syntax. It’s god awful for someone coming from a C-like background, but that seems like a very temporary problem. I went from hating Haskell’s syntax so much I wanted a language wrapping it, to realizing it’s actually rather well thought out and other languages have a lot of noise to them. Lots of curly braces and semicolons that seem to mostly be there to make writing a compiler easier. I still love C-like syntax, and still don’t like Haskell’s syntax, but not enough to write a CoffeeScript-like language.
- jfhufl 9y agoFunny, I love Ruby and it's my primary language of choice, but I'm a heretic, using braces whenever I can: # events per day ndbh.fetch('select ts from tbl').all.map { |row| row[:ts] }.map { |t| t.strftime('%Y-%m-%d') }.each { |day| days[day] += 1 } (yes this could be conciser) This is generally frowned on, and I'm sure everyone who likes having this style foo .map {} .select {} .blah {} will frown as well, but it's so much more visually appealing to me, coming from a C/perl background when I first encountered Ruby (many years ago). Funny how important syntax is. So easy to look at code in another style/language and go THATS NOT RIGHT!!
- strkek 9y ago> Funny how important syntax is. So easy to look at code in another style/language and go THATS NOT RIGHT!! I'm waaay too picky in this aspect. I once was reading about Ada (before my bias towards C-like languages kicked in). It looked interesting and all, and I was like "Hmm, not bad, learning this might be interesti--" > Put_Line Nope. The irony here is, my current language of choice is Go, and tests must be named like `TestType_Method` and I'm like "arrrgggghhh". EDIT: Btw I don't really mind indentation styles, brace placement, "dot placement" (like your example) and those things, as long as they're consistent across the codebase.
- pessimizer 9y ago> "more modern" Is it really touted as that? It's just a more Algol-looking way to write for Erlang semantics.
- eric_the_read 9y agoIt's more than that; there's a lot of tooling and documentation around Elixir that either doesn't exist in Erlang or appears to exist only via tribal knowledge ("Well, of course everyone knows you need to {X} to {Y}, why waste time writing it down?"). My experience trying to write a gen_server that could be upgraded on-line in Elixir was vastly simpler than trying to figure it out in Erlang. That said, I think Elixir has helped inspire the Erlang community to get better at documentation and tooling, so that's a definite win for both.
- bitwalker 9y agoThe other reply mentioned tools and documentation, which are the big differentiators in my opinion, but there are also some language level tools in Elixir which feel more "modern" I guess, the macro system which is on part with Lisp, protocols, a consistent standard library which extends Erlang rather than replacing it (although it does duplicate some things, both because binaries are the default string type in Elixir, and because OTP has a bit of an annoying problem with switching the position of the subject), and some conveniences like pipelines, the `with` construction - basically it produces something which semantically is like Erlang at its core, but how you go about solving problems in it offers a lot more options, which oftentimes result in a lot less code written for the same effect.
- pratikkonnur 9y agoWe dabble in Akka - a good option for Java / Scala shops
- WorldMaker 9y agoThere are ports of Akka and Akka-like libraries for a number of languages. Actor frameworks are relatively easy to find for most languages. Another variation of a sort of an Actor framework that gets used for relatively large projects is the Orleans framework for .NET: http://dotnet.github.io/orleans/ http://dotnet.github.io/orleans/
- Cthulhu_ 9y agoHow does it compare to Go channels? I only know actors from Scala, Go felt similar but more er, simple?
- sudhirj 9y agoSimple to fault, in that channels and Goroutines don’t prevent you from making mistakes the way actors do - ie you can still share state and memory. Go allows for the possibility of writing cleanly concurrent code, but you must study and practice. Using an actor system, though, involves a bit more study with regards to setting things up, but it’s almost impossible to make mistakes - actors simply can’t access each other’s state (maybe some systems allow it, but you’ll have to struggle against the language to make that mistake).
- macintux 9y agoErlang does a great job of protecting data from concurrency problems, but you're still vulnerable to mistakes like deadlock.
- signa11 9y ago> Erlang does a great job of protecting data from concurrency problems, but you're still vulnerable to mistakes like deadlock. without any shared data, and an asynchronous-message-passing style concurrency (i.e sharing memory by communicating, as opposed to communicating by sharing memory) can you please elucidate how we might get into deadlocks ? thanks !
- macintux 9y agoOTP offers two basic messaging options: async or synchronous. The latter is implemented by forcing the caller to wait for the response. If process A sends a synchronous message to B, and B while processing it sends a different synchronous message either directly to A or to another process that eventually calls back to A, you end up in a deadlocked state.
- nickbauman 9y agoI'd argue the most interesting problems are those that end up having some sort of shared state requirement. Clojure handles this nicely with language primitives like atoms and refs, which helps avoid the pitfalls of the locking paradigm found in Go and Java, for example. What does D do in this case, then?
- earenndil 9y agoThere are the basic primitives found in c/any language, but there are also synchronized{} (or synchronized (my_mutex) {}) blocks that essentially automagically handle locking/unlocking and let you just mutate around willy-nilly across threads (in that block). There are also atomics, although the syntax for using them is a bit clumsy (foo.atomicOp!"+="(7), anyone?).
- nkim12 9y agorust has actors too https://github.com/actix/actix https://github.com/actix/actix
- eikenberry 9y agoLibraries aren't the same as native support as the latter case influences all uses of concurrency in a language while the former only affects things that use that library and may be incompatible with other concurrency libraries.
- zokier 9y agoThat seems kinda fallacious. I don't think inherently there is anything that would make an ecosystem around a library weaker than an ecosystem around a language. I.e. if you have a popular actor library then it is not unreasonable to think that there is as rich if not richer ecosystem of things built on top of that library than a niche language with native actors.
- syeaj 9y agoWould any sort of actor model language have the raw throughput for a AAA game? I get how this model is nice for the web, for example, but I'm wondering why it doesn't get used for high performance computing (that I know of).
- macintux 9y agoThe Erlang VM (BEAM) is unusual in that it, like the languages it hosts, is very opinionated. It is designed for robustness, scalability, concurrency, distributed environments. And immutable data. So far as I know, you literally cannot implement a language with mutability on the VM. So, raw performance will never be its thing. In general, I think the actor model could achieve high performance, but perhaps only if messaging is syntactic instead of truly distributed with mailboxes, network transparency, etc.
- mapcars 9y ago>So far as I know, you literally cannot implement a language with mutability on the VM. Elixir allows reassignment, of course, in the end, it uses different "Erlang" variables, but the language abstracts it.
- macintux 9y agoChanging an alias isn't the kind of mutability that would allow high performance, though.
- masklinn 9y agoThat is not mutability.
- erikpukinskis 9y agoIt is though. A mutable state layer written on an immutable backend still has all the pitfalls of a native mutable data structure.
- defined 9y agoErlang does go way beyond concurrency into the domain of distributed and highly-reliable systems, and has more than lessons for us IMO - together with OTP, it’s still arguably one of the most effective and battle-tested environments for highly concurrent systems. Like anything, it has its envelope and sweet spots, as well as its warts. Some people find the Erlang language syntax to be ugly; I quite like it and find it easier to comprehend in general than Elixir, which is much more verbose to my eyes (except for the very nice pipe operator!) Yes, the semicolon / period thing in Erlang can be annoying, but not so much that it really gets in my way. The tooling can be a real barrier to entry, and I’m glad that Elixir is bringing more people into the fold, although I can’t bring myself to really like Elixir for purely subjective reasons. For example, one of my major complaints about Elixir is that it is literally Erlang with some syntax changes, rewritten libraries, and extensions like macros - but if you don’t grok Erlang thoroughly, you will get frustrated when you hit the bottom layer - like exceptions and error messages - and it’s full of Erlang data structures. I had the same reaction when working with non-Java languages targeting the JVM. They all feel a bit like bolt-on kits. At some point, you really need to understand the core. In my opinion, of course.
- jordigh 9y agoHey, if you're curious about anything else I wrote in that blog post, just ask me. I thought the concurrency was a big highlight, but there's lots of other parts of D that I would love to talk about. As I said, the whole thing was very enjoyable. I had lots of fun using D. :-)
- agumonkey 9y agoyeah but what about recursive named emacs kmacros ?! jk nice post
- psyc 9y agoIn the distant past, I did low-level multithreaded programming in the patchwork style of synchronizing access to various data. Partly because the codebase already did it that way and partly because i didn’t know better. These days I would never torture myself with such a thing. I still do concurrency sans framework, but like in the article - lock free with no sharing. Copying a block of data when it’s ready is cheap compared to computing it.
- beached_whale 9y agoIn addition, you can break your program down into flows of tasks up front. Thinking about the synchronization explicitly helps too, often there is too much unnecessary communication. I think that the data structures used by D for message passing are the real interesting part here. Akin to not using raw pointers, don't use raw data structures to convey state updates and let the owner of that state do it themselves with a queue or some abstraction for messages.
- zoffix222 9y agoI like concurrency in Rakudo Perl 6. (https://docs.perl6.org/language/concurrency https://docs.perl6.org/language/concurrency) Especially the .hyper method that turns a regular sequence into hyper sequence that gets processed in parallel. First 5000 prime numbers: # single-thread, takes 14.893s put ^∞ .grep(*.is-prime).head: 5000 # multi-threaded, takes 8.853s on my 2-core box put ^∞ .hyper.grep(*.is-prime).head: 5000
- jordigh 9y agoConcurrency isn't the same thing as parallelism. You can have concurrency without ever executing two things at the same time. You just give some CPU cycles to one threaad, then pause it, give cycles to the other thread, and keep switching back and forth. Indeed, this is what any multitasking operating system has always done, even when every computer only had one CPU. For parallelism as what you describe, D instead has approximately the same thing. There's a `parallel` function in the standard library that works on any range (i.e. any iterable). You basically instead of doing foreach(object; somerange) { // .. } do foreach(object; parallel(somerange)) { // ... } and it works like your Perl example. If you want it more functionalish, without writing out the loop, you can use `taskPool.map` instead of ordinary `map` for the same effect. More details: stdlib: https://dlang.org/phobos/std_parallelism.html https://dlang.org/phobos/std_parallelism.html Programming in D chapter on parallelism: http://ddili.org/ders/d.en/parallelism.html http://ddili.org/ders/d.en/parallelism.html
- zzzcpan 9y agoConcurrency is not a first class citizen in Perl 6 though. As with Perl 5 the best you can hope for is actor model implemented on top of promises/futures.
- zoffix222 9y agoWhat would make it a first-class citizen? Even an empty Rakudo Perl 6 program is a multi-threaded program, as the dynamic optimizer runs on a separate thread. I'd figure if that's possible, concurrency/parallelism would be quite a first-class citizen, not a best hope on top of something.
- John_KZ 9y agoI'm not a professional programmer, but I had no problems with Java's multithreading libraries. Lock handling, synchronization etc are a pain in the ass to get used to but not so much of a big deal, and I don't see how D is any different than that.
- glic3rinu 9y agoWith the actor model you don't share state between threads, hence no need to lock anything. Each variable can only be updated by a single and unique thread. If another thread (or actor) wants to modify it, it has to send a modification request messages to that one thread to do the job. That's quite a difference! Edit: Java threading model is fine for limited amounts of concurrency. Complexity escalates quickly with highly concurrent applications; the actor model makes reasoning about those programs far easier.
- John_KZ 9y agoSo it's like a lock, except you call it "modification request". Great, got it.
- glic3rinu 9y agoSimilar in the sense that they protect data from being accessed by multiple threads at once. But the implementation and semantics are quite different. The main practical drawback, I would say, is that locks may suffer from the mutual exclusion problem; where you have contention, starvation and deadlocking. Also your ability to parallelize operations over shared state quickly vanishes as the number of threads grow...
- Jtsummers 9y agoThe difference is that it doesn't halt your execution unless you want it to. With shared memory and locks you have to wait when you set a value if anyone else is interacting with that object or that part of the object (if you have fine-grained locks). So you wait both for getting and setting. If your system behavior does not immediately depend on the value of what you're changing, then actors are fantastic. You don't wait, it's async. You send the data to the controlling actor (which is an object in the OO sense itself). Your process continues on as if nothing changed until you need the updated results. Process A B ! {update, x, newVal}, %% A bunch of work B ! {lookup, y, A}, % where y perhaps depended on x Y = receive % Here we finally wait, if B is processing a lot of requests {B,y,Val} -> Val end, %% more work Process B loop(State) -> receive {update, Var, Val} -> loop(update(State,Var,Val)); {lookup, Var, Pid} -> Pid ! lookup(State, Var), loop(State) end. Where B may be much more complex than that, in this case it looks like a simple KV store. The receive at the end of Process A is blocking and is the only time we end up with a lock in Process A. So if A never needs to do anything but send values to B, then A can run undisturbed by everyone else's data requests. This lends itself well to certain pipelined and dataflow architectures.
- rad_gruchalski 9y agoTry actors.
- gbrown 9y agoFor a C++ implementation of actor patterns, see: https://www.actor-framework.org/ https://www.actor-framework.org/
- KingMob 9y agoI'm surprised nobody's mentioned Clojure in the comments. Its concurrency abilities are top-notch, straightforward to use, and immutable data makes it dead-simple to avoid issues.
- jscipione 9y agoBeOS had an threading implementation like this in C++ in 1999: https://www.haiku-os.org/legacy-docs/bebook/TheKernelKit_ThreadsAndTeams_Overview.html https://www.haiku-os.org/legacy-docs/bebook/TheKernelKit_Thr...
- dnautics 9y agoI used to program in this, and it was clearly inspired by the actor model (in retrospect, now that I have been programming in elixir for a while). Lots of void* passing, though. Initially the actor model was designed in the 70s.
- moomin 9y agoPeople always used to ask me for advice on multithreading, because I had a reputation for multithreaded code that worked. I recommended they use Retlang (there's a Java version called Jetlang), which works on a shared-nothing message passing basis (and is blindingly fast). They tended to say they didn't want a library and just wanted to use locks and threads. I said that not doing that was how come I had a reputation for correctness.
- bsder 9y agoThey're in Java and writing locks and threads. Sigh. java.util.concurrent is probably the single best concurrency library I have ever used. Bar none. When I'm writing in Java, and I find myself writing a lock, my first reaction now is: "This is okay for exploration, but which data structure from java.util.concurrent am I going to have swap in here when it's all done. And can I do it now and save myself a lot of grief." It only took me 7+ years to come to this realization. Apparently I'm a slow learner. :(
- 2sk21 9y agoMy small brush with this: back in the summer of 1990, I did a summer internship with Doug Lea who led the JSR 166 group that led to java.util.concurrent. My work was on a concurrency library for a parallel computer called the Encore Multimax
- MrBuddyCasino 9y ago> summer of 1990 some thread corrupted this date
- cup-of-tea 9y agoThey're using Java instead of Clojure. Sigh.
- joncampbelldev 9y agoI'm saying this as a massive clojure fanboy: java.util.concurrent is an amazing set of concurrency utilities and is the building block for many of clojure's concurrency features. It's definitely worth knowing when writing performant java AND clojure (you can wrap the little bit you need so you don't have to scatter java interop all over your codebase). Sometimes a (swap! atom fn) doesn't cut it.
- tytytytytytytyt 9y agotl;dr Guy discovers message passing and actors.
- alfanerd 9y agoIn my 30+ years experience of concurrency, the shared-nothing message model makes it relative easy to reason about concurrency (since every actor is single threaded). I have worked with Java, C# and Python, and for each I have developed a concurrent object framework, making it quite easy to work with multible channels of incoming events, for example for process control systems with barcode-scanners, scales, I/O-busses etc. connected to the system I think ponylang does a pretty good implementation of the Actor model. Feel free to have a look at the Python impl. of the framework (www.github.com/pylots/pyworks)
- pjmlp 9y agoIn a way it is ironic that we went full speed into threads, shared memory and plugins, only to go back into processes, messages and sandboxing as a means to tame multi-core programming and safety.
- zokier 9y agoI thought threads and shared memory were introduced more as performance hacks than superior architecture. It is not surprising that as the performance profile has changed (due both increased core counts and improved software primitives), we are getting back those things that were deemed too expensive previously
- platz 9y agoConcurrency abstractions are still built on primitives that involve locks and memory barriers.
- pjmlp 9y agoJust like the old UNIX process IPC or micro-kernels communication is.
- sametmax 9y agoBut don't we have thread pools and futures for that ? You can't get a simpler API.
- agentultra 9y agoObligatory, but Haskell, comment. Nice post, looks like a decent API. I wonder how similar each implementation of this model in Language X is and how easily it would be to test their implementation meet the spec.
- kccqzy 9y agoWhat about Haskell? Haskell has great support for writing code in the Actor style if you wish. But you can also have traditional locks and simple mutable pointers in Haskell too.
- agentultra 9y agoIt’s great for writing concurrent code that is both easy to understand and be confident in its correctness. I just didn’t want to derail the conversation away from D and the authors’ discovery.
- herodotus 9y agoWhen Steve Jobs was away due to his illness, Bertrand Serlet presented Mac OS 10.6 (Snow Leopard) at the WWDC June 2009 with the startling slide that said "0 new features". There was sustained applause from the crowd. But in fact there was a very significant under-the-hood new feature called "Grand Central Dispatch". It is a low-level API that removes a lot of the pain of multithreading. Essential for OS X and iOS developers, as it turns out.
- FPGAhacker 9y agoDear God I wish they would do another 0 new features release. It used to be that the excuse was bug fixes don’t sell. Maybe not, but buggy software does lose customers. They may bring in the newbies with features, but you retain with bug fixes.
- overcast 9y agoI really don't think the general population abandons entire operating system ecosystems, because of buggy software.
- pythonaut_16 9y agoI think in part they do, otherwise OS X wouldn't have gained as nearly as much marketshare on the promise that "It just works"
- overcast 9y agoOS X gained market share because of the insane popularity of the iPod, and subsequently the iPhone. Gained nearly 4% post iPhone launch.
- 0x00000000 9y agoThere are other negative effect as well. People will do everything they can to not get the latest updates. See: Windows 10
- eecc 9y agoOn the other hand an Actor modelled implementation can - will - quickly get out of hand if you start using it to directly represent a business domain. Actor signature is Any -> Unit which essentially means “computes anything” plus there’s internal state (become), message queue size, problems with deterministic testability and so on.. I think Actors as an alternative to Threads, particularly valuable for its distributed supervisor trees, and used to build more abstract consumers for a strongly typed model of business representations.
- sametmax 9y agoI don't see what's so good with that. Don't we have multiprocessing and threading pools that does that stuff automatically ?
- zmmmmm 9y agoA shout out to the GPars framework [1] which makes actor based coding stunningly easy in Groovy / Java. I'm kind of shocked by the performance I can get out of it, to be honest. I once assumed that for trivial tasks (reading lines from a file, transforming them, writing out result etc), a parallel library would lose a lot of overhead in thread synchronisation. However I find that I can trivially construct data flows that massively outperform single threaded C code in Groovy. Which is to say, whatever overhead there is from thread synchronisation is dwarfed by the benefit of parallelising reading, computation and writing. If I had to think hard about concurrency to do it (as I would in C) I would probably never attempt it for simple programs, but GPars makes it so easy, safe and completely portable that there's really no reason not to. [1] http://www.gpars.org/ http://www.gpars.org/
- bjoli 9y agoIt amazes me to no end that CML-style concurrency is not used more often. (Even though Go is sort of almost there) I have been using guile fibers for some time, and it is a marvel! Not only can I write most code as if it was single threaded, but I can mix fibers code with pthreads and use fibers message passing when I need it.