timeline

Multithreading in Modern C++

With the new C++11 Standard, C++ faces the first-time challenges of multicore architectures. The 2011 published standard defines how a C++ program has to behave in the presence of multiple threads. The C++11 multithreading capabilities are composed of two components. On the one hand, this is the defined memory model, and on the other hand, the standardized threading interface.

Timeline

 

A well-defined memory model

The defined memory model is the necessary basis so that multithreaded programming makes sense in C++. Thus, the memory model has to give answers to the following questions.

  1. What are atomic operations?
  2. Which order of operations is ensured?
  3. When are the memory effects of operations visible?

To 1: Atomic operations are operations that follow the first three letters of the famous ACID Idioms from the database theory. Atomic operations are atomic (A), going from one consistent (C) state to the next, and are executed in isolation (I). In particular, this means that no other thread can observe an intermediate state of an atomic operation. The incrementation atomVar++ shows the consistency and isolation of an atomic operation very nicely. If atomVar is an atomic variable, atomVar can have only the old or the new value. The consistency of the variable atomVar is that it only changes from one state to the other, and the isolation is that another thread cannot observe any intermediate value.

To 2: The compiler, which translates the program into assembler instructions, and the processor that executes the assembler instructions, can rearrange the operations. Mostly this is done for performance reasons. In addition, the various tiers of storage (cache) offer the possibility of providing the result of the operations in a delayed way.

To 3: Since one thread may see an operation on a variable later than another, the threads must obey specific rules.

The standardized threading interface

The standardized threading interface in C++11 is composed of the following components.

  1. Threads
  2. Tasks
  3. Thread local data
  4. Condition variables

To 1: Threads are the basic building blocks of multithreaded programming. They do their work autonomously, are parameterized by arguments, and interact with other threads via shared variables.

To 2:Tasks is a relatively modern concept. Tasks consist of two components, which are connected by a communication channel. As an endpoint of the channel, one component produces the result while the other endpoint consumes it. The producer is called Promise, the consumer Future.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    To 3: Thread local data is data – such as it is easy to guess from the name- that explicitly belongs to one thread.

    To 4:Condition variables enables it to implement producer/consumer workflows. The consumer waits for the notification of the producer so that he can continue his work.

    What will come with C++17 and C++20?

    The next C++ standards are planned for 2017 and 2020. C++17 and C++20  will consist of many extensions around the multithreading capabilities of the existing standard. Because the existing functionality is very basic. These changes will likely include the following three exciting features:

    1. Latches and barriers
    2. Transactional memory
    3. Automatically parallelizing or vectorizing algorithms of the Standard Template Library (STL)

    To 1: Latches and barriers are similar to semaphores.

    To 2:Transactional memory is, in simple words, the idea of ACID applied (again, only the first three letters) to code. That means the code is annotated as transactional memory and optimistically executed without synchronization with other threads. At the end of the transaction, the results will only be published if the initial conditions are still valid. If not, the outcome of the result is rejected, and the transaction is again executed. While mutexes always lock the critical area, the transaction is not locked, but the result may be discarded. A critical area is a section of code that, at most, one thread can enter at a time.

    To 3: While parallelizing algorithms distribute the operations on their containers on multiple threads, vectorizing algorithms perform their operations on several container elements in a single step.

    My Plan

    In the following few articles, I will look deeper into the components of the C++  memory model and the standardized threading interface. My focus is not to elaborate on every detail. The details are very well documented in the current C++ standard 14882:2014 or in the webpage cppreference.com.

    My focus will particularly be on the following few articles to show you typical errors in dealing with multithreaded programs and suggest solutions. For this purpose, I will incorporate as much theory to understand the problem and the solution as necessary. I start with the standardized threading interface.

    What’s next?

    In the next post, I will deal with the creation of threads.

     

     

     


    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *