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 the 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.

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).
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 mean, 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, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Wolfgang Gärtner, Louis St-Amour, Stephan Roslen, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Avi Kohn, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, and Peter Ware.
Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, and Sudhakar Belagurusamy.
Seminars
I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.
Bookable (Online)
Deutsch
English
Standard Seminars
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
New
Contact Me
Modernes C++,

Comments
thats why i havce read it completely
site and I blieve this site is real informative!
Keep on posting.
was searching for thoughts on this matter last couple of days.
I'll be sure to bookmark it and return to read more of your useful information. Thanks
for the post. I'll certainly return.
condVar.wait(lck,[]{return dataReady;});
RSS feed for comments to this post