Search code examples
multithreadingc++11thread-synchronization

How to achieve thread synchronization in two C++11 Threads?


Situation:

A Factory class member function manager spawns two worker threads. Each worker thread run a loop for each day of an year, and increment their own var workHours, and a shared var work. At the end of each day, each worker thread signals manager to get their report of work and workHours When the manager gets workHours from both the worker threads, it reports totalWorkHours as (workHours[0] + workHours[1]) to the boss

Problem:

Threads are not working as expected. Threads are running in multiple loops before reporting to the manager. They are not synced for each day reporting. How to achieve this thread syncing, that after each day, both threads report their stats to manager.

Code:

[The below code is has no compile errors, in case you want to test run, edit]

#include <iostream>
#include <string>
#include <thread>
#include <mutex>

using namespace std;

class Factory{

public:
    Factory() {
        work = 0; 
        workHours[0] = 0; 
        workHours[1] = 0;
    };
    void manager();

private:
    std::thread threads[2];
    std::mutex m_lock;
    std::condition_variable cond[2];

    int work;               // Shared across threads
    int workHours[2];       // One var for each thread
    int totalWorkHours;

    void worker(int id);
    void boss(int work, int workHours);
};

void Factory::worker(int id)
{
    cout<< id <<" Started Working "<<endl;
    for (int day = 0; day<365; day++)
    {
        std::unique_lock<std::mutex> lck{ m_lock };
        work++;
        workHours[id]++;
        cout << id << " working " << endl;
        cond[id].notify_one();
        lck.unlock();
        std::this_thread::sleep_for(1s);
    }
}


void Factory::manager()
{
    int wHrs0, wHrs1;

    threads[0] = std::thread([&](Factory *fac) { fac->worker(0); }, this);
    threads[1] = std::thread([&](Factory *fac) { fac->worker(1); }, this);

    //for (int day=0; day<365; day++)
    for (;;)
    {
        std::unique_lock<mutex> lck0{ m_lock };
        cond[0].wait(lck0);
        cout << "Worker0 workHours : " << workHours[0] << "  Total Work : " << work << endl;
        wHrs0 = workHours[0];
        lck0.unlock();

        std::unique_lock<mutex> lck1{ m_lock };
        cond[1].wait(lck1);
        cout << "Worker1 workHours : " << workHours[1] << "  Total Work : " << work << endl;
        wHrs1 = workHours[1];
        lck1.unlock();

        totalWorkHours = wHrs0 + wHrs1;

        cout << "Both Workers Worked one day" << endl;
        boss(work, totalWorkHours);
    }
}

void Factory::boss(int work, int workHours)
{
    cout << "I am not Happy with just " << work << " amount of work in damn " << workHours << " Hrs " << endl;
}

int main()
{
    Factory nike;
    nike.manager();

    //wait for keypress
    cin.get();

    return 0;
}

Solution

  • I solved it myself. Posting here for reference.

    worker waits for manager to consume the work result. And manager waits for both workers to finish one day work.

    // Example program
    #include <iostream>
    #include <string>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    
    using namespace std;
    
    class Factory{
    
    public:
        Factory() {
            work = 0; 
            workHours[0] = 0; 
            workHours[1] = 0;
        };
        void manager();
    
    private:
        std::thread threads[2];
        std::mutex m_lock;
        std::condition_variable cond[2];
        std::condition_variable condMan;
        bool resFetch[2];
        bool resAvailable[2];
    
        int work;               // Shared across threads
        int workHours[2];       // One var for each thread
    
        int totalWorkHours;
    
        void worker(int id);
        void boss(int work, int workHours);
    };
    
    void Factory::worker(int id)
    {
        cout<< id <<" Started Working "<<endl;
        for (int day = 0; day<365; day++)
        {
            std::unique_lock<std::mutex> lck{ m_lock };
            while(!resFetch[id])
                condMan.wait(lck);
    
            resFetch[id] = false;
    
            work++;
            workHours[id]++;
            cout << id << " working " << endl;
            resAvailable[id] = true;
            cond[id].notify_one();
            lck.unlock();
    
            std::this_thread::sleep_for(1s);
        }
    }
    
    void Factory::manager()
    {
        int wHrs0, wHrs1;
    
        threads[0] = std::thread([&](Factory *fac) { fac->worker(0); }, this);
        threads[1] = std::thread([&](Factory *fac) { fac->worker(1); }, this);
    
        for (;;)
        {
            std::unique_lock<std::mutex> lck{ m_lock };
    
            resFetch[0] = true;
            resFetch[1] = true;
    
            condMan.notify_all();
    
            while(!resAvailable[0])
                cond[0].wait(lck);
            cout << "Worker0 workHours : " << workHours[0] << "  Total Work : " << work << endl;
            wHrs0 = workHours[0];
            resAvailable[0] = false;
    
            while (!resAvailable[1])
                cond[1].wait(lck);
            cout << "Worker1 workHours : " << workHours[1] << "  Total Work : " << work << endl;
            wHrs1 = workHours[1];
            resAvailable[1] = false;
    
            lck.unlock();
    
            totalWorkHours = wHrs0 + wHrs1;
    
            cout << "Both Workers Worked one day" << endl;
            boss(work, totalWorkHours);
        }
    }
    
    void Factory::boss(int work, int workHours)
    {
        cout << "I am not Happy with just " << work << " amount of work in damn " << workHours << " Hrs " << endl;
    }
    
    int main()
    {
        Factory nike;
        nike.manager();
    
        //wait for keypress
        cin.get();
    
        return 0;
    }