Lightweight Concurrency: Threads are on a Diet

Most modern programming languages are adding lightweight concurrency capabilities. Why is this happening? It is a response to the multicore revolution. You need more parallelism in your applications and you need it without adding a great overhead.

"Sugar threads or diet threads? Task-based programming, please."

James Reinders offered eight key rules for multicore programming based on parallel programming experiences of his own and others. I have been successfully following his eight key rules with just a few minor changes. I needed some changes a few times in order to adapt the rules to the limitations imposed by each different framework and programming language.

His rule #3 suggests programming in tasks (chores), not threads (cores). However, many popular high-level programming languages have been offering threads to support concurrency. Therefore, in order to follow this rule in C# 3.0 or Java 6, you needed to create your own abstraction layer, handling the thread/core management for you. This was indeed complex.

Furthermore, there is another big problem. Threads are expensive. Creating a new thread requires processing time and additional memory. For this reason, there is an important overhead when using threads. If the overhead introduced is equal or greater than the benefit, it will encourage you to avoid threads.

You can use a pool of threads, to avoid creating new threads for each new task to be distributed into different cores. However, this technique can transform a simple task-based algorithm into many complex synchronization activities to distribute each task to a different thread. A nice object-oriented design combined with a thread management layer can add exciting task-based capabilities to your existing frameworks. It is a bit complex to achieve this goal in C# 3.0, as shown in an article I had published a few months ago "Simplifying Parallelism Complexity in C#".

In the last two years, most modern programming languages began adding lightweight concurrency capabilities. They are offering different ways of creating tasks (chores) and not threads (cores). They are adding features that simplify developers to follow James Reinders' rule #3.

On the one hand, you can take advantage of multi-core using processes and threads. These are known as heavyweight concurrency. On the other hand, you can work with tasks, fibers and messages between actors, known as lightweight concurrency.

Functional programming is back because it offers an excellent alternative to create task-based algorithms with lightweight concurrency mechanisms.

.Net Framework 4.0 with its Parallel Extensions, still in Beta 1, adds the possibility to work with Tasks. It replaces the old heavyweight treading model with a new lightweight threading model. Tasks add less overhead than threads.

Apple's Grand Central Dispatch, explained by James Reinders a few days ago (http://www.ddj.com/go-parallel/blog/archives/2009/06/apples_grand_ce.html) also takes the lightweight concurrency route.

Ruby 1.9 added Fibers, lightweight concurrency to favor a task-based approach.

GParallelizer adds lightweight concurrency to Groovy, running on the JVM.

The aforementioned moves to lightweight concurrency are just a few examples. There are many other frameworks and languages adding lightweight concurrency capabilities.

The lightweight concurrency era is the next step to evolve programming languages to the multi-core age. This will allow applications to take advantage of more than 8 cores using task-based algorithms and reducing the concurrency overhead. The language architects are working hard to help you follow James Reinders' rule #3.

Parallel Pattern 5: Stencil
All memory addresses used for reads are expressed as offsets
Distributing Work Across Cores Using .NET
A roll-your-own ThreadPool implementation
Looking For The Lost Packets: Part 2
Looking For The Lost Packets: Part 1

Real World Parallelism Webinar Series
  • February 18, 2010
    Lock Contention, Using Intel Parallel Studio to Improve Performance
    Speaker: Vasanth Tovinkere, Software Engineer, Intel Corporation (Bio)

    Vasanth Tovinkere is a software engineer in the Developer Products Division (DPD) at Intel. His current role involves defining novel approaches to understanding and visualizing parallel performance and consulting with strategic customers to help them prepare and deliver code for the multicore world. Vasanth has been involved in the development of automatic semantic event detectors for digital sports technologies in Intel Labs. He also has been awarded three patents and has two patents pending.

    Abstract:
    Discover how easy it is to use the power of Microsoft Visual Studio and Intel Parallel Studio to find performance issues due to lock contention in threaded applications. This ensures that shipped applications can take better advantage of multicore processors. In this webcast, we provide live demonstrations that show how to identify lock contentions issues with Visual Studio and Intel Parallel Studio, an add-in to Visual Studio that helps developers create fast, reliable code on multicore processors.t.