The Atomic Boolean

Contents[Show]

The remaining atomics - in contrast to std::atomic_flag - are partial or full specialisations of the class template std::atomic. Let's start with std::atomic<bool>.

 

std::atomic<bool>

std::atomic<bool> has a lot more to offer than std::atomic_flag. It can explicitly be set to true or false. That's enough to synchronise two threads. So I can simulate condition variables with atomic variables.

Let's first have a look at condition variables.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// conditionVariable.cpp

#include <condition_variable>
#include <iostream>
#include <thread>
#include <vector>

std::vector<int> mySharedWork;
std::mutex mutex_;
std::condition_variable condVar;

bool dataReady;

void waitingForWork(){
    std::cout << "Waiting " << std::endl;
    std::unique_lock<std::mutex> lck(mutex_);
    condVar.wait(lck,[]{return dataReady;});
    mySharedWork[1]= 2;
    std::cout << "Work done " << std::endl;
}

void setDataReady(){
    mySharedWork={1,0,3};
    {
        std::lock_guard<std::mutex> lck(mutex_);
        dataReady=true;
    }
    std::cout << "Data prepared" << std::endl;
    condVar.notify_one();
}

int main(){
    
  std::cout << std::endl;

  std::thread t1(waitingForWork);
  std::thread t2(setDataReady);

  t1.join();
  t2.join();
  
   for (auto v: mySharedWork){
      std::cout << v << " ";
  }
      
  
  std::cout << "\n\n";
  
}

 

And now the pendant with atomic booleans.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// atomicCondition.cpp

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>

std::vector<int> mySharedWork;
std::atomic<bool> dataReady(false);

void waitingForWork(){
    std::cout << "Waiting " << std::endl;
    while ( !dataReady.load() ){             // (3)
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
    mySharedWork[1]= 2;                      // (4)
    std::cout << "Work done " << std::endl;
}

void setDataReady(){
    mySharedWork={1,0,3};                    // (1)
    dataReady= true;                         // (2)
    std::cout << "Data prepared" << std::endl;
}

int main(){
    
  std::cout << std::endl;

  std::thread t1(waitingForWork);
  std::thread t2(setDataReady);

  t1.join();
  t2.join();
  
  for (auto v: mySharedWork){
      std::cout << v << " ";
  }
      
  
  std::cout << "\n\n";
  
}

 

What guarantees, that line 17 will be executed after line 14? Or to say it more general, that the thread t1 will execute mySharedWork[1]= 2 (line 17) after thread t2 had executed mySharedWork={1,0,3} (line 22). Now it gets more formal.

  • Line22 (1) happens-before line 23 (2)
  • Line 14 (3) happens-before line 17 (4)
  • Line 23 (2) synchronizes-with line 14 (3)
  • Because happens-before is transitive, it follows: mySharedWork={1,0,3} (1) happens-before mySharedWork[1]= 2 (4)

In want to explicitly mention one point. Because of the condition variable condVar or the atomic dataReady, the access to the shared variable mySharedWork is synchronised. This holds although mySharedWork is not protected by a lock or itself an atomic.

Both programs produce the same result for mySharedWork.

conditionVariableAtomic

Push versus pull principle

Obviously, I cheated a little. There is one difference between the synchronisation of the threads with the condition variable and the atomic boolean. The condition variable notifies the waiting thread (condVar.notify()), that it should proceed with its work. But the waiting thread with the atomic boolean checks, if the sender is done with its work (dataRead= true).

The condition variable notifies the waiting thread (push principle). The atomic boolean repeatedly asks for the value (pull principle).

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Stay informed about my mentoring programs.

 

 

Subscribe via E-Mail.

compare_exchange_strong and compare_exchange_weak

std::atomic<bool> and the fully or partially specialisations of std::atomic supports the bread and butter of all atomic operations: compare_exchange_strong. This function has the syntax: bool compare_exchange_strong(T& expected, T& desired). Because this operation compares and exchanges in one atomic operation a value, is often called compare_and_swap (CAS). This kind of operation is in a lot of programming languages available. Of course, the behaviour may differ a little.

A call of atomicValue.compare_exchange_strong(expected, desired) obeys the following strategy. In case the atomic comparison of atomicValue with expected returns true, the value of atomicValue is set in the same atomic operation to desired. If the comparison returns false, expected will be set to atomicValue. The reason why the operation compare_exchange_strong is called strong is simple. There is a method compare_exchange_weak. This weak version can spuriously fail. That means, although *atomicValue == expected holds, the weak variant returns false. So you have to check the condition in a loop: while ( !atomicValue.compare_exchange_weak(expected, desired) ). The reason for the weak form is performance. On some platforms, the weak is faster than the strong variant.

What's next?

The next post will be about the class template std::atomic. So I write about the different specialisations for integrals and pointer. They provide a richer interface than the atomic boolean std::atomic<bool>. (Proofreader Alexey Elymanov

 

 

 

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, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, 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, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, and Ann Shatoff.

 

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

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

Comments   

-2 #11 Carissa 2017-03-02 12:28
Thanks for sharing such a good idea, article is fastidious,
thats why i havce read it completely
Quote
-3 #12 Norine 2017-03-08 07:20
I was reading some of your posts on this intternet
site and I blieve this site is real informative!
Keep on posting.
Quote
-3 #13 Jenni 2017-04-16 22:52
Hello.This article was extremely remarkable, particularly since I
was searching for thoughts on this matter last couple of days.
Quote
-2 #14 Bobbie 2017-04-18 04:12
Hello There. I found your blog using msn. This iss a really well written article.
I'll be sure to bookmark it and return to read more of your useful information. Thanks
for the post. I'll certainly return.
Quote
-3 #15 thunderwiiring 2017-06-06 20:12
I like your content! very much!!
Quote
0 #16 Venkat 2020-05-03 18:18
Line 17, correction needed? void() returning value?
condVar.wait(lck,[]{return dataReady;});
Quote
0 #17 Andrej Podzimek 2020-09-11 18:48
The .load() is unnecessary when you don't specify a non-default memory order.
Quote

Mentoring

Stay Informed about my Mentoring

 

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 4060

Yesterday 5317

Week 4060

Month 148231

All 11629385

Currently are 249 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments