Enforce concurrent modification of a variable (C++)
I'm trying to unit test an atomic library (I am aware that an atomic library is not suitable for unit testing, but I still want to give it a try)
For this, I want to let X parallel threads increment a counter and evaluate the resulting value (it should be X).
The code is below. The problem is that is it never breaks. The Counter
always nicely ends up being 2000 (see below). What I also notice is that the cout
is also printed as a whole (instead of being mingled, what I remember seeing with other multithreaded couts
)
My question is: why doesn't this break? Or how can I let this break?
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
bool start = false;
int Counter = 0;
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
std::cout << "Incrementing in thread " << std::this_thread::get_id() << std::endl;
Counter++;
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 2000; ++i) {
threads.push_back(std::thread(Inc));
}
// signal the threads to start
{
std::lock_guard<std::mutex> lk(m);
start = true;
}
cv.notify_all();
for (auto& thread : threads) {
thread.join();
}
// Now check whether value is right
std::cout << "Counter: " << Counter << std::endl;
}
The results looks like this (but then 2000 lines)
Incrementing in thread 130960
Incrementing in thread 130948
Incrementing in thread 130944
Incrementing in thread 130932
Incrementing in thread 130928
Incrementing in thread 130916
Incrementing in thread 130912
Incrementing in thread 130900
Incrementing in thread 130896
Counter: 2000
Any help would be appreciated
UPDATE: Reducing the nr of threads to 4, but incrementing a million times in a for loop (as suggested by @tkausl) the cout
of thread id appear to be sequential..
UPDATE2: Turns out that the lock had to be unlocked to prevent exclusive access per thread (lk.unlock()
). An additional yield
in the for-loop increased the race condition effect.
c++ multithreading atomic googletest
add a comment |
I'm trying to unit test an atomic library (I am aware that an atomic library is not suitable for unit testing, but I still want to give it a try)
For this, I want to let X parallel threads increment a counter and evaluate the resulting value (it should be X).
The code is below. The problem is that is it never breaks. The Counter
always nicely ends up being 2000 (see below). What I also notice is that the cout
is also printed as a whole (instead of being mingled, what I remember seeing with other multithreaded couts
)
My question is: why doesn't this break? Or how can I let this break?
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
bool start = false;
int Counter = 0;
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
std::cout << "Incrementing in thread " << std::this_thread::get_id() << std::endl;
Counter++;
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 2000; ++i) {
threads.push_back(std::thread(Inc));
}
// signal the threads to start
{
std::lock_guard<std::mutex> lk(m);
start = true;
}
cv.notify_all();
for (auto& thread : threads) {
thread.join();
}
// Now check whether value is right
std::cout << "Counter: " << Counter << std::endl;
}
The results looks like this (but then 2000 lines)
Incrementing in thread 130960
Incrementing in thread 130948
Incrementing in thread 130944
Incrementing in thread 130932
Incrementing in thread 130928
Incrementing in thread 130916
Incrementing in thread 130912
Incrementing in thread 130900
Incrementing in thread 130896
Counter: 2000
Any help would be appreciated
UPDATE: Reducing the nr of threads to 4, but incrementing a million times in a for loop (as suggested by @tkausl) the cout
of thread id appear to be sequential..
UPDATE2: Turns out that the lock had to be unlocked to prevent exclusive access per thread (lk.unlock()
). An additional yield
in the for-loop increased the race condition effect.
c++ multithreading atomic googletest
Too many threads which do too little. Try with two or four threads which each increment the variable one million times in a loop.
– tkausl
Nov 21 '18 at 14:18
You are trying to produce an heisenbug. There is no 100% way.
– Detonar
Nov 21 '18 at 14:23
@tkausl Tnx, see update
– Ben
Nov 21 '18 at 14:28
coliru.stacked-crooked.com/a/4f0b8c68851694c6
– tkausl
Nov 21 '18 at 14:35
add a comment |
I'm trying to unit test an atomic library (I am aware that an atomic library is not suitable for unit testing, but I still want to give it a try)
For this, I want to let X parallel threads increment a counter and evaluate the resulting value (it should be X).
The code is below. The problem is that is it never breaks. The Counter
always nicely ends up being 2000 (see below). What I also notice is that the cout
is also printed as a whole (instead of being mingled, what I remember seeing with other multithreaded couts
)
My question is: why doesn't this break? Or how can I let this break?
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
bool start = false;
int Counter = 0;
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
std::cout << "Incrementing in thread " << std::this_thread::get_id() << std::endl;
Counter++;
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 2000; ++i) {
threads.push_back(std::thread(Inc));
}
// signal the threads to start
{
std::lock_guard<std::mutex> lk(m);
start = true;
}
cv.notify_all();
for (auto& thread : threads) {
thread.join();
}
// Now check whether value is right
std::cout << "Counter: " << Counter << std::endl;
}
The results looks like this (but then 2000 lines)
Incrementing in thread 130960
Incrementing in thread 130948
Incrementing in thread 130944
Incrementing in thread 130932
Incrementing in thread 130928
Incrementing in thread 130916
Incrementing in thread 130912
Incrementing in thread 130900
Incrementing in thread 130896
Counter: 2000
Any help would be appreciated
UPDATE: Reducing the nr of threads to 4, but incrementing a million times in a for loop (as suggested by @tkausl) the cout
of thread id appear to be sequential..
UPDATE2: Turns out that the lock had to be unlocked to prevent exclusive access per thread (lk.unlock()
). An additional yield
in the for-loop increased the race condition effect.
c++ multithreading atomic googletest
I'm trying to unit test an atomic library (I am aware that an atomic library is not suitable for unit testing, but I still want to give it a try)
For this, I want to let X parallel threads increment a counter and evaluate the resulting value (it should be X).
The code is below. The problem is that is it never breaks. The Counter
always nicely ends up being 2000 (see below). What I also notice is that the cout
is also printed as a whole (instead of being mingled, what I remember seeing with other multithreaded couts
)
My question is: why doesn't this break? Or how can I let this break?
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
bool start = false;
int Counter = 0;
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
std::cout << "Incrementing in thread " << std::this_thread::get_id() << std::endl;
Counter++;
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 2000; ++i) {
threads.push_back(std::thread(Inc));
}
// signal the threads to start
{
std::lock_guard<std::mutex> lk(m);
start = true;
}
cv.notify_all();
for (auto& thread : threads) {
thread.join();
}
// Now check whether value is right
std::cout << "Counter: " << Counter << std::endl;
}
The results looks like this (but then 2000 lines)
Incrementing in thread 130960
Incrementing in thread 130948
Incrementing in thread 130944
Incrementing in thread 130932
Incrementing in thread 130928
Incrementing in thread 130916
Incrementing in thread 130912
Incrementing in thread 130900
Incrementing in thread 130896
Counter: 2000
Any help would be appreciated
UPDATE: Reducing the nr of threads to 4, but incrementing a million times in a for loop (as suggested by @tkausl) the cout
of thread id appear to be sequential..
UPDATE2: Turns out that the lock had to be unlocked to prevent exclusive access per thread (lk.unlock()
). An additional yield
in the for-loop increased the race condition effect.
c++ multithreading atomic googletest
c++ multithreading atomic googletest
edited Nov 21 '18 at 15:24
Ben
asked Nov 21 '18 at 14:15
BenBen
7371427
7371427
Too many threads which do too little. Try with two or four threads which each increment the variable one million times in a loop.
– tkausl
Nov 21 '18 at 14:18
You are trying to produce an heisenbug. There is no 100% way.
– Detonar
Nov 21 '18 at 14:23
@tkausl Tnx, see update
– Ben
Nov 21 '18 at 14:28
coliru.stacked-crooked.com/a/4f0b8c68851694c6
– tkausl
Nov 21 '18 at 14:35
add a comment |
Too many threads which do too little. Try with two or four threads which each increment the variable one million times in a loop.
– tkausl
Nov 21 '18 at 14:18
You are trying to produce an heisenbug. There is no 100% way.
– Detonar
Nov 21 '18 at 14:23
@tkausl Tnx, see update
– Ben
Nov 21 '18 at 14:28
coliru.stacked-crooked.com/a/4f0b8c68851694c6
– tkausl
Nov 21 '18 at 14:35
Too many threads which do too little. Try with two or four threads which each increment the variable one million times in a loop.
– tkausl
Nov 21 '18 at 14:18
Too many threads which do too little. Try with two or four threads which each increment the variable one million times in a loop.
– tkausl
Nov 21 '18 at 14:18
You are trying to produce an heisenbug. There is no 100% way.
– Detonar
Nov 21 '18 at 14:23
You are trying to produce an heisenbug. There is no 100% way.
– Detonar
Nov 21 '18 at 14:23
@tkausl Tnx, see update
– Ben
Nov 21 '18 at 14:28
@tkausl Tnx, see update
– Ben
Nov 21 '18 at 14:28
coliru.stacked-crooked.com/a/4f0b8c68851694c6
– tkausl
Nov 21 '18 at 14:35
coliru.stacked-crooked.com/a/4f0b8c68851694c6
– tkausl
Nov 21 '18 at 14:35
add a comment |
1 Answer
1
active
oldest
votes
cv.wait(lk, {return start; });
only returns with the lk
acquired. So it's exclusive. You might want to unlock lk
right after:
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
lk.unlock();
Counter++;
}
And you must remove std::cout
, because it potentially introduces synchronization.
thecouts
seem to mix after a while. Haven't seen an inconsistent counter though.
– Ben
Nov 21 '18 at 15:06
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53414039%2fenforce-concurrent-modification-of-a-variable-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
cv.wait(lk, {return start; });
only returns with the lk
acquired. So it's exclusive. You might want to unlock lk
right after:
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
lk.unlock();
Counter++;
}
And you must remove std::cout
, because it potentially introduces synchronization.
thecouts
seem to mix after a while. Haven't seen an inconsistent counter though.
– Ben
Nov 21 '18 at 15:06
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
add a comment |
cv.wait(lk, {return start; });
only returns with the lk
acquired. So it's exclusive. You might want to unlock lk
right after:
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
lk.unlock();
Counter++;
}
And you must remove std::cout
, because it potentially introduces synchronization.
thecouts
seem to mix after a while. Haven't seen an inconsistent counter though.
– Ben
Nov 21 '18 at 15:06
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
add a comment |
cv.wait(lk, {return start; });
only returns with the lk
acquired. So it's exclusive. You might want to unlock lk
right after:
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
lk.unlock();
Counter++;
}
And you must remove std::cout
, because it potentially introduces synchronization.
cv.wait(lk, {return start; });
only returns with the lk
acquired. So it's exclusive. You might want to unlock lk
right after:
void Inc() {
// Wait until test says start
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, {return start; });
lk.unlock();
Counter++;
}
And you must remove std::cout
, because it potentially introduces synchronization.
answered Nov 21 '18 at 14:47
felixfelix
1,510314
1,510314
thecouts
seem to mix after a while. Haven't seen an inconsistent counter though.
– Ben
Nov 21 '18 at 15:06
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
add a comment |
thecouts
seem to mix after a while. Haven't seen an inconsistent counter though.
– Ben
Nov 21 '18 at 15:06
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
the
couts
seem to mix after a while. Haven't seen an inconsistent counter though.– Ben
Nov 21 '18 at 15:06
the
couts
seem to mix after a while. Haven't seen an inconsistent counter though.– Ben
Nov 21 '18 at 15:06
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
@Ben On my computer(E5-1620), it gives ~1999, but mostly 2000.
– felix
Nov 21 '18 at 15:12
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
Adding a sleep to the for-loop (see update) helps. It's now finally not working :)
– Ben
Nov 21 '18 at 15:15
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53414039%2fenforce-concurrent-modification-of-a-variable-c%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Too many threads which do too little. Try with two or four threads which each increment the variable one million times in a loop.
– tkausl
Nov 21 '18 at 14:18
You are trying to produce an heisenbug. There is no 100% way.
– Detonar
Nov 21 '18 at 14:23
@tkausl Tnx, see update
– Ben
Nov 21 '18 at 14:28
coliru.stacked-crooked.com/a/4f0b8c68851694c6
– tkausl
Nov 21 '18 at 14:35