Enforce concurrent modification of a variable (C++)












0















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.










share|improve this question

























  • 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
















0















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.










share|improve this question

























  • 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














0












0








0








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.










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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



















  • 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












1 Answer
1






active

oldest

votes


















3














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.






share|improve this answer
























  • 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











  • Adding a sleep to the for-loop (see update) helps. It's now finally not working :)

    – Ben
    Nov 21 '18 at 15:15











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
});


}
});














draft saved

draft discarded


















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









3














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.






share|improve this answer
























  • 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











  • Adding a sleep to the for-loop (see update) helps. It's now finally not working :)

    – Ben
    Nov 21 '18 at 15:15
















3














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.






share|improve this answer
























  • 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











  • Adding a sleep to the for-loop (see update) helps. It's now finally not working :)

    – Ben
    Nov 21 '18 at 15:15














3












3








3







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.






share|improve this answer













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.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 21 '18 at 14:47









felixfelix

1,510314




1,510314













  • 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











  • 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











  • @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




















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

ComboBox Display Member on multiple fields

Is it possible to collect Nectar points via Trainline?