5

I have a small problem and wondering if someone can help. I tried to demonstrate my problem in the most simple way possible. I am trying to pass an object by reference to multiple threads. Every thread calls "doSomething" which is a member function that belongs to the object "Example". The "doSomething" function should increment the counter. My gcc version is 4.4.7

The questions:

why is the value of the variable "counter" is not incremented although I passed the object by reference to the thread function.

The code:

#include <iostream>
#include <thread>

class Exmaple {
    private:
        int counter;

    public:
            Exmaple() { 
            counter = 0;
        }    

        void doSomthing(){
            counter++;
        }

        void print() {
            std::cout << "value from A: " << counter << std::endl;
        }

};

// notice that the object is passed by reference
void thread_task(Exmaple& o) {
    o.doSomthing();
    o.print();
}    

int main()
{
    Exmaple b;
    while (true) {
        std::thread t1(thread_task, b);
        t1.join();
    }    
    return 0;
}

The output:

value from A: 1
value from A: 1
value from A: 1
value from A: 1
value from A: 1
value from A: 1
value from A: 1
value from A: 1
value from A: 1
Saad Zaamout
  • 656
  • 1
  • 5
  • 10

3 Answers3

7
while (true) {
    std::thread t1(thread_task, b);
    t1.join();
}  

Two things you need to know here:

  • Use std::ref to pass a reference.
  • Infinite loop is Undefined Behavior in C++;

Working Example below:

#include <iostream>
#include <thread>

class Exmaple {
    private:
        int counter;

    public:
            Exmaple() { 
            counter = 0;
        }    

        void doSomthing(){
            counter++;
        }

        void print() {
            std::cout << "value from A: " << counter << std::endl;
        }

};

// notice that the object is passed by reference
void thread_task(Exmaple& o) {
    o.doSomthing();
    o.print();
}    

int main()
{
    Exmaple b;
    for(int i =0; i < 10; i++) {
        std::thread t1(thread_task, std::ref(b));
        t1.join();
    }    
    return 0;
}

Output:

value from A: 1
value from A: 2
value from A: 3
value from A: 4
value from A: 5
value from A: 6
value from A: 7
value from A: 8
value from A: 9
value from A: 10

See it Live.

Though going further you should also consider data race

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
  • You need `atomic_int` or some other means to synchronise. – juanchopanza Feb 09 '17 at 23:18
  • I'm curious, I didn't know infinite loops where undefined behavior in C++, what would be the "correct" way to write such a loop? – ShadowMitia Feb 09 '17 at 23:23
  • @juanchopanza You are right. Though for the OP's *specific* example I didn't think it necessary to go into that. Added the concern. Thanks – WhiZTiM Feb 09 '17 at 23:27
  • 1
    @ShadowMitia, A correct program in C++ is assumed to terminate, so the correct way to write the loop or any loop is to always test a condition that that will someday exit the loop. Please see the answers to [this](http://stackoverflow.com/questions/3592557/optimizing-away-a-while1-in-c0x/3593551#3593551) SO thread – WhiZTiM Feb 09 '17 at 23:36
  • @WhiZTiM this is a very good example, thank you! For completeness, would you mind adding a mutex to the class? Threads should acquire lock, then increment counter, then release it. – CraigDavid Mar 28 '20 at 04:26
4

I'm not very familiar with multithreading, but you're passing b by value to the thread, and not by reference. The value of b is then passed by reference to thread_task, so the value is always 1.

According to the documentation, you have to write your thread like so to pass objects by reference:

std::thread t1(thread_task, std::ref(b));
ShadowMitia
  • 2,411
  • 1
  • 19
  • 24
1

Note that in std::thread t1(thread_task, b) you pass b by value to the constructor of std::thread (as you call the constructor here, and not thread_task directly). A solution could be to either to wrap b by an std::ref-object, or change your code to pass a pointer:

void thread_task(Exmaple* o) {
    o->doSomthing();
    o->print();
}

int main()
{
    Exmaple b;
    while (true) {
        std::thread t1(thread_task, &b);
        t1.join();
    }
    return 0;
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58