Search code examples
c++multithreadingfor-loopmutexrvalue

Using rvalue in the condition statement of `for` loop in C++


I am working on a programming task that takes in an n value and uses three threads to print zero, even and odd numbers.

For example, if n = 3, it will print 010203. I encountered a strange behavior that when rvalue is used in the condition check in a for loop, my program just hangs entirely without doing anything. This phenomenon only appears in multithreading program. Thus, I put the entire code here:

#include <functional>  // function
#include <iostream>
#include <mutex>   // mutex
#include <thread>  // thread

using namespace std;

class ZeroEvenOdd {
  private:
    int n;
    mutex zero_mtx, even_mtx, odd_mtx;

  public:
    ZeroEvenOdd(int n) {
        this->n = n;
        zero_mtx.unlock();

        even_mtx.lock();
        odd_mtx.lock();
    }

    // printNumber(x) outputs "x", where x is an integer.
    void zero(function<void(int)> printNumber) {
        for (int i = 1; i <= this->n; ++i) {
            zero_mtx.lock();

            printNumber(0);

            if (i & 1) {  // odd
                odd_mtx.unlock();

            } else {  // even
                even_mtx.unlock();
            }
        }
    }

    void even(function<void(int)> printNumber) {
        for (int a = 2; a <= ((this->n) + 1); a += 2) { // NOTE: `(this->n) + 1` is an rvalue
            even_mtx.lock();

            printNumber(a);

            zero_mtx.unlock();
        }
    }

    void odd(function<void(int)> printNumber) {
        for (int b = 1; b <= ((this->n) + 1); b += 2) { // NOTE: `(this->n) + 1` is an rvalue
            odd_mtx.lock();

            printNumber(b);

            zero_mtx.unlock();
        }
    }
};

// g++ 1116.cpp -std=c++11 -pthread -O0 && ./a.out
int main() {
    printf("helloworld\n");

    ZeroEvenOdd zod(3);

    auto printNumber = [](int x) { cout << x; };

    thread threadA([&]() { zod.zero(printNumber); });
    thread threadB([&]() { zod.even(printNumber); });
    thread threadC([&]() { zod.odd(printNumber); });

    threadA.join();
    threadB.join();
    threadC.join();

    return 0;
}

The above program can be compiled and run with g++ 1116.cpp -std=c++11 -pthread -O0 && ./a.out, but the program just hangs without doing anything. However, if I change the lines

for (int a = 2; a <= ((this->n) + 1); a += 2) { // NOTE: `(this->n) + 1` is an rvalue
for (int b = 1; b <= ((this->n) + 1); b += 2) { // NOTE: `(this->n) + 1` is an rvalue

to

for (int a = 2; a <= ((this->n) + 0); a += 2) {
for (int b = 1; b <= ((this->n) + 0); b += 2) {

then everything works as expected.

I've tried to reproduce this phenomenon in single thread program, but this behavior doesn't present in single thread program.

I am not sure if this behavior has anything to do with multithreading. I just provide every evidence I have for people to investigate.

Update: Change typo lvalue to rvalue.


Solution

  • Although locking and unlocking of the mutexes in different threads is an issue, it is not the issue.

    When the loop control is <= ((this->n) + 1), the program proceeds as follows:

    1. parent locks even and locks odd;
    2. threadB waits for even;
    3. threadC waits for odd;
    4. threadA locks zero, emits '0', unlocks odd and waits for zero;
    5. threadC locks odd, emits '1', unlocks zero and waits for zero;
    6. threadA locks zero, emits '0', unlocks even and waits for zero;
    7. threadB locks even, emits '2', unlocks zero and waits for even;
    8. threadA locks zero, emits '0', unlocks zero and exits;
    9. threadC locks odd, emits '3', unlocks zero and exits.

    threadB never exits and waits for ever because (a += 2) == 4 and ((this->n) + 1) == 4.

    Nothing is emitted because the output remains buffered.