42

I am interested in learning concurrent programming, focusing on the application/user level (not system programming). I am looking for a modern high level programming language that provides intuitive abstractions for writing concurrent applications. I want to focus on languages that increase productivity and hide the complexity of concurrent programming.

To give some examples, I don't consider a good option writing multithreaded code in C, C++, or Java because IMHO my productivity is reduced and their programming model is not intuitive. On the other hand, languages that increase productivity and offer more intuitive abstractions such as Python and the multiprocessing module, Erlang, Clojure, Scala, etc. would be good options.

What would you recommend based on your experience and why?

EDIT: Thanks everybody for your interesting answers. It's hard to make a conclusion without actually trying since there are many good candidates: Erlang, Clojure, Scala, Groovy, and perhaps Haskell. I voted the answer with the most convincing arguments, but I'll try all the good candidates before deciding which one to pick :)

11
  • 21
    To give an example, I don't consider a good option writing multithreaded code in C, C++, or Java. Why? On the other hand, Python and the multiprocessing module, Erlang, Clojure, Scala, etc. are some of my options. Again, why? Expand your question to better define what you're actually looking for.
    – yannis
    Nov 22, 2011 at 18:17
  • 2
    So do you want to learn parallel programming with all the gotchas or you want to hide some of its complexity and focus on productivity?
    – MaR
    Nov 22, 2011 at 18:24
  • @MaR Focus on productivity and hide the complexity :)
    – sakisk
    Nov 22, 2011 at 18:34
  • Just note that many important concepts are avoided(some may say solved) in some of these languages and therefore C is really the best language to learn concurrency. (Or at least it seems so to me; I don't know enough about the all the languages listed). Increasing productivity is often conflicting with comprehensive learning.
    – user606723
    Nov 22, 2011 at 23:10
  • 1
    @DeadMG Decreased productivity is their problem. I don't want to focus on the syntax of the language instead of the problem. I definitely don't want to end up struggling with deadlocks. As a simple example, I want to use simple statements such as begin transaction end transaction and everything inside should be deadlock-free and either succeed or fail as a whole.
    – sakisk
    Nov 23, 2011 at 18:43

16 Answers 16

33

You almost certainly should look at Clojure - in my opinion it's the best modern language for multi-core programming and it is extremely productive.

Key attributes:

  • It's a functional language, which is a boon for both concurrency and your ability to develop using higher level abstractions. It features fully immutable persistent data structures and lazy sequences which will be familiar to anyone with experience in functional languages like Haskell.
  • It features a very novel software transactional memory system for concurrent lock-free access to mutable state. Making code concurrency-safe is often as simple as wrapping it in a (dosync....) block.
  • It's a Lisp - which makes it extremely powerful for macro-based metaprogramming and code generation. This can bring significant productivity advantages (essay by Paul Graham - "Beating The Averages")
  • It's a JVM language - so not only do you get access to the huge array of libraries and tools in the Java ecosystem, you also benefit from the huge engineering effort that has gone into making the JVM an effective platform for concurrent server-side applications. For practical purposes, this gives it a huge advantage over languages that don't have this kind of foundation to build upon.
  • It's dynamic - which results in very concise code and a lot of productivity. Note however that you can use optional static type hints for performance if needed.
  • The language is designed around abstractions which is somewhat hard to explain but the net effect is that you get a set of relatively orthogonal features that you can combine to solve your problems. An example would be the sequence abstraction, which enables you to write code that deals with every "sequential" type of object (which includes everything from lists, strings, Java arrays, infinite lazy sequences, lines being read from a file etc.)
  • There's a great community - helpful, insightful but most importantly very pragmatic - the focus in Clojure is generally on "getting things done".

Some mini code samples with a concurrency slant:

;; define and launch a future to execute do-something in another thread
(def a (future (do-something)))

;; wait for the future to finish and print its return value
(println @a)

;; call two functions protected in a single STM transaction
(dosync
  (function-one)
  (function-two))

In particular, it's worth watching one or more of these videos:

8
  • 23
    The purpose of static type declarations in strongly-typed languages is not to "improve performance where needed," and I'm getting kind of sick of Lisp advocates trotting out that old strawman. Type declarations have two purposes: to provide certain compile-time guarantees of correctness, and to make the code easier to read, especially for someone other than the original author. The inherently better performance that static typing provides is just a bonus. Nov 23, 2011 at 17:44
  • 8
    I've had to work with another developer's JavaScript code lately at work, and that's the most painful part of the process: with no types on the function arguments, I have to hunt throughout the entire codebase to figure out what they're supposed to be and what they can do based on where they're called from. This would be a non-issue if JavaScript had retained C's type system in addition to its general syntax. Nov 23, 2011 at 17:45
  • 1
    @MasonWheeler: IMHO if you can't figure out how to call a function without type annotations, it's a problem with the documentation (or lack thereof). Even in duck typed languages everything usually has to satisfy some structural type constraints (e.g. must support arithmetic operations, must be iterable, must be indexable, etc.). Static types would only help minimally because they wouldn't give much hint about what the function does.
    – dsimcha
    Nov 24, 2011 at 0:50
  • 2
    @Mason I never said that there weren't other advantages to static type declarations. In fact I like static type declarations for exactly the reasons you state. However I also like the productivity gains of dynamic typing. It's a trade-off. If you have a good test suite, I generally find that this mitigates a lot of the downsides of dynamic typing both in terms of ensuring correctness and providing example code to help newcomers understand correct usage. YMMV.
    – mikera
    Nov 25, 2011 at 11:40
  • 1
    @dsimcha - the alternative to designing around abstractions would be to design around a concrete implementation. For example, most of the old Lisp functions worked only on linked lists stored in cons cells. You needed different functions for different data structures. In Clojure, the core library function work on anything sequential (as in the answer).
    – mikera
    Nov 25, 2011 at 11:43
27

You could try D. It offers three models. I recommend either the first or second.

  1. std.concurrency. If you use this module for all your concurrency needs, then a combination of the language and the standard library enforces isolation between threads. Threads primarily communicate via message passing, with limited support for shared memory in a way that favors "safety first" and disallows low-level data races. Unfortunately the documentation of std.concurrency needs improvement, but the model is documented in a free chapter of Andrei Alexandrescu's book, "The D Programming Language".

  2. std.parallelism. This module is designed specifically for multicore parallelism rather than general-case concurrency. (Concurrency and parallelism are not the same thing, though concurrency is necessary to implement parallelism.) Since the whole point of parallelism is performance, std.parallelism doesn't make any isolation guarantees because they would make writing efficient parallel code difficult. However, it does abstract away a lot of error-prone low-level details so that it's very hard to screw up if you're parallelizing workloads that you've manually verified are mutually independent.

  3. core.thread is a low-level wrapper over OS-specific threading APIs. Both std.concurrency and std.parallelism use it under the hood, but I would only recommend using it if you're writing your own concurrency library or find some ridiculous corner case that can't be done well in either std.parallelism or std.concurrency. No one should use something this low-level for day-to-day work.

4
  • You should have mentionned the immutability/purity, the thread local storage by default and the shared which impose mutation in sequential order. Thoses are languages support missing in C/C++ to write concurent code.
    – deadalnix
    Nov 24, 2011 at 8:42
  • @deadalnix: To me most of those are details of the std.concurrency model (how isolation is enforced). I wanted to keep this post concise.
    – dsimcha
    Nov 24, 2011 at 13:01
  • Well actually no. Concurency require both library AND language support.
    – deadalnix
    Nov 24, 2011 at 20:16
  • @deadalnix: Right, but they were put in place largely to support std.concurrency.
    – dsimcha
    Nov 25, 2011 at 3:37
23

Erlang is definitely a great option, but something a little more practical might be Go, Google's new language.

It's not so far from other common languages, so it's typically easy to get if you already know other 'easy' languages. Many people compare it with Python or even Lua in terms of how 'comfortable' is it to program.

8
  • @faif is asking about application/user level, not systems concurrent programming. How Erlang fits this?
    – Chiron
    Nov 22, 2011 at 21:28
  • @Raynos: Depends on the community. Nov 23, 2011 at 11:17
  • 1
    @Chiron: Erlang is a programming language, it's used to create applications. Typically, multitprocessing applications. I don't know where does it fit as 'systems concurrent proramming', I haven't heard of any OS written in Erlang.
    – Javier
    Nov 23, 2011 at 13:56
  • 1
    After taking a quick look in the Go tutorial I want to say that IMHO a language with a C-like syntax which uses (limited) pointers is definitely not a modern language that increases productivity.
    – sakisk
    Nov 23, 2011 at 18:40
  • 1
    check, check, check.
    – Javier
    Jun 3, 2013 at 18:17
23

Take a look at Microsoft's Parallel Programming for .net. It is very intuitive.

Many personal computers and workstations have two or four cores (that is, CPUs) that enable multiple threads to be executed simultaneously. Computers in the near future are expected to have significantly more cores. To take advantage of the hardware of today and tomorrow, you can parallelize your code to distribute work across multiple processors. In the past, parallelization required low-level manipulation of threads and locks. Visual Studio 2010 and the .NET Framework 4 enhance support for parallel programming by providing a new runtime, new class library types, and new diagnostic tools. These features simplify parallel development so that you can write efficient, fine-grained, and scalable parallel code in a natural idiom without having to work directly with threads or the thread pool. The following illustration provides a high-level overview of the parallel programming architecture in the .NET Framework 4... http://i.msdn.microsoft.com/dynimg/IC292903.png

3
  • +1 This is a exactly what he is asking for. Though, when problems do occur, it will be difficult to debug them without an understanding of concurrency at a lower level. Not to mention, taking this on as a C# beginner could prove...interesting. Nov 22, 2011 at 18:40
  • @P.Brian.Mackey - I agree. However that is not uncommon, it wouldn't be a stretch to compare this to using ORMs when one does not fully understand relational model and SQL... Nov 22, 2011 at 18:46
  • 1
    Especially PLINQ. While it's useful only for a small subset of tasks, it can be very easy to use.
    – svick
    Nov 24, 2011 at 17:33
21

Both Erlang and Scala have actor-based concurrency, which I found very intuitive and easy to learn.

The Actor model in computer science is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent digital computation: in response to a message that it receives, an actor can make local decisions, create more actors, send more messages, and determine how to respond to the next message received... It has been used both as a framework for a theoretical understanding of computation, and as the theoretical basis for several practical implementations of concurrent systems.

19

I'm learning about Haskell right now, and reading this paper has convinced me that Haskell is a good option for concurrent programming. Because it's purely functional (the type system knows whether a function does any input, output, or reading/modification of global state), it can do things like Software Transactional Memory (summed up very nicely in the above paper) which behaves similarly to transactions in databases - you get a bunch of nice things like atomicity with only a little extra sugar. AFAIK, Haskell threads are very light-weight too. In addition to these things, the fact that Haskell is purely functional lets even simple tasks be run in parallel with little more than a single keyword (par). source

8

Google's GO language has some interesting tools for concurrency - that would be another fun thing to try. See: http://golang.org/doc/effective_go.html#concurrency and read down a bit for examples.

Concurrent programming is a large topic and there is space only for some Go-specific highlights here.

Concurrent programming in many environments is made difficult by the subtleties required to implement correct access to shared variables. Go encourages a different approach in which 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. To encourage this way of thinking we have reduced it to a slogan:

Do not communicate by sharing memory; instead, share memory by communicating.

This approach can be taken too far. Reference counts may be best done by putting a mutex around an integer variable, for instance. But as a high-level approach, using channels to control access makes it easier to write clear, correct programs.

One way to think about this model is to consider a typical single-threaded program running on one CPU. It has no need for synchronization primitives. Now run another such instance; it too needs no synchronization. Now let those two communicate; if the communication is the synchronizer, there's still no need for other synchronization. Unix pipelines, for example, fit this model perfectly. Although Go's approach to concurrency originates in Hoare's Communicating Sequential Processes (CSP), it can also be seen as a type-safe generalization of Unix pipes...

6

In the next version, C# makes it even easier than that diagram shows. There are two new keywords Async and Await.

Async is used as a function modifier and says "this operation performs its work on another thread.

Await is used within an Async function, and this is where the magic happens. Basically Await tells the compiler to run the operation following the keyword in a seperate thread and wait for the results. Any code after the await call runs after the operation.

ALSO, the operation synchronizes to the calling thread (so if you are doing an asynch operation in response to a button click, you don't have to manually post back to the UI thread). Two little keywords and you get a lot of concurrency power. Read more here

3
  • Note any decent OS C# compiler already supports C#5, async and await.
    – Raynos
    Nov 23, 2011 at 4:14
  • Basically Await tells the compiler to run the operation following the keyword in a seperate thread and wait for the results. I wonder if this answer is correct - async await is not about threads. This article explains is nice: There is no thread
    – sventevit
    Aug 6, 2018 at 19:35
  • Good point. I guess I was speaking too simply about it. What actually happens is a "continuation" is made that subscribes to the event of the task in the "await" being completed. And yes certain I/O operations and thread.sleep() (which basically responds to a clock interrupt) do not have a thread. but what about manually made Tasks that do no I/O like lets say we made an awaitable fibonacci calculator? Technically the article is right "There is no thread" but in reality there never was, it was always a concept we used to hide away the details of what the OS was doing for us. Aug 7, 2018 at 15:33
6

I'd still recommend C++. It is more than capable of the necessary abstractions to write decent concurrent code. The overwhelming probability is that you simply have a poor library for doing the job, since good libraries for doing the job are relatively new, and indeed, the knowledge for using C++ well is not exactly common. Intel's TBB has only been around for a few years, and Microsoft's PPL has only shipped since last year.

If you use something like TBB or PPL, then concurrent code is, well, not exactly trivial to write, insofar that concurrency is never trivial, but far from arduous. If you use pthreads or Win32 threads directly, then it's no wonder you don't like it- you're practically writing in assembler with such functions. But with the PPL, then you're talking about standard functional algorithms that are parallelised for you, generic data structures for concurrent access, and that sort of good stuff.

2
  • 1
    Check out Boost.Threads or C++0x std::thread (or std::tr1::thread). It's actually a very good abstraction, IMO.
    – greyfade
    Nov 23, 2011 at 17:31
  • 1
    @greyfade: They have absolutely nothing on PPL or TBB. boost::thread is just an OS wrapper with a little RAII. PPL and TBB are real concurrent algorithms, containers, etc.
    – DeadMG
    Nov 25, 2011 at 11:04
6

A plug for Ada is needed here, as it has all the top level abstractions for parallelism & concurrency. otherwise known as tasking. Also as OP asked for intuitive (a subjective criteria!) i think a different approach to the java-centric world might be appreciated.

0
5

I would suggest Groovy/Java/GPars if you can be JVM based since it allows for actors, dataflow, communicating sequential processes (CSP), data parallelism, software transactional memory (STM), agents, ... The point here is that there are many high-level concurrency and parallelism models each of which has different "sweet spots". You do not want to use a model that is not in harmony with the solution to a problem you are trying to construct. Languages and frameworks with only one model coerce you into algorithm hacking.

Of course I might be seen as biased as I am contributor to Groovy and GPars. On the other hand I do work with CSP and Python, cf. Python-CSP.

A further point is that the original question is about learning, not about writing a production system. So the Groovy/Java/GPars combination is a good way of learning even if the eventual production work is done in C++ using something like Just::Thread Pro or TBB rather than being JVM based.

(Some perfectly reasonable URL links had to be removed because of some panic about spamming by the host site.)

1
4

What about Clojure? You can use Swing for example but enjoying Clojure concurrent programming facility? Clojure has a pretty well Java integration.

Also, have you considered Java 7 Fork/Join framework?

2

You may also want to look at Groovy and the GPars library. GPars BTW is somewhat similar to .NET Parallel Extension mentioned in another answer, but Groovys flexible syntax makes it read better in some circumstances.

0

Scala has been mentioned several times in the questions and in answers, but I have seen no reference to Akka which is an actor implementation that can be used with both Scala and Java.

1
  • What's wrong with this answer? No other answer mentioned akka, and akka implements a high-level abstraction for concurrent programming.
    – Giorgio
    Jun 3, 2013 at 12:41
-1

I think it depends on what it is you're building. Desktop apps, or server? I've heard that (but don't have personal experience) node.js great for concurrent programming for servers (both in terms of writing code and in performance). If I wanted to write a new server app, I'd probably try that out. Not sure about desktop apps... I've written a fair amount of stuff in C# and there are some tools that hide the complexity nicely, though for other cases you have to deal with it head-on.

-1

I may get hit over the head for this, but have you read chapter 7 of TAOUP? The section I'm thinking of specifically is threads vs processes. I've found that the concept of concurrent processing makes most people think of threads, yet I've never seen an instance where a thread is easier and faster to use than spawning a child process.

You are farming out all the details of handling concurrency to the smart guys who built your OS. There's already lots of communication methods in place, and you don't have to worry about locking of shared resources. Basically, threads are an efficiency hack, which falls under the rule of optimization. Don't optimize if you haven't tested for necessity.

Find a good subprocess library, such as envoy for python. Or you could just write several separate programs in C, and write another "master" program to use fork and pipe to spawn and communicate with the subprocesses.

1
  • 3
    This is the opposite of what OP explicitly wants … spawning a process is just as low-level as spawning a thread manually. OP is interested in high-level abstractions of concurrency. Mar 12, 2012 at 18:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.