mardi 2 janvier 2018

Resolve deadlock issue, waiting in the main thread for multiple worker threads to finish (C++11)

I'm trying to write a program with c++11 in which multiple threads are run, and, during each cycle the main thread will wait for each thread to be finished. The program below is a testing program for this concept.

Apparently I'm missing something trivial in my implementation as it looks like I'm experiencing a deadlock (Not always, just during some random runs).

#include <iostream>
#include <stdio.h>
#include <thread>
#include <chrono>
#include <condition_variable>
#include <mutex>

using namespace std;

class Producer
{
public:
    Producer(int a_id):
        m_id(a_id),
        m_ready(false),
        m_terminate(false)
    {
        m_id = a_id;
        m_thread = thread(&Producer::run, this);
        // ensure thread is available before it is started
        this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    ~Producer() {
        terminate();
        m_thread.join();
    }

    void start() {
        //cout << "start " << m_id << endl;
        unique_lock<mutex> runLock(m_muRun);
        m_ready = true;
        runLock.unlock();
        m_cond.notify_all();
    }

    void wait() {
        cout << "wait " << m_id << endl;
        unique_lock<decltype(m_muRun)> runLock(m_muRun);
        m_cond.wait(runLock, [this]{return !m_ready;});
    }

    void terminate() {
        m_terminate = true;
        start();
    }

    void run() {
        do {
            unique_lock<decltype(m_muRun)> runLock(m_muRun);
            m_cond.wait(runLock, [this]{return m_ready;});

            if (!m_terminate) {
                cout << "running thread: " << m_id << endl;
            } else {
                cout << "exit thread: " << m_id << endl;
            }

            runLock.unlock();
            m_ready = false;
            m_cond.notify_all();
        } while (!m_terminate);
    }

private:
    int m_id;
    bool m_ready;
    bool m_terminate;
    thread m_thread;

    mutex m_muRun;
    condition_variable m_cond;
};

int main()
{
    Producer producer1(1);
    Producer producer2(2);
    Producer producer3(3);

    for (int i=0; i<10000; ++i) {
        cout << i << endl;
        producer1.start();
        producer2.start();
        producer3.start();

        producer1.wait();
        producer2.wait();
        producer3.wait();
    }

    cout << "exit" << endl;
    return 0;
}

The program's output when the deadlock is occurring:

....
.......
running thread: 2
running thread: 1
wait 1
wait 2
wait 3
running thread: 3

Looking at the program's output when the deadlock occurs, I suspect the bottleneck of the program is that sometimes the Producer::wait function is called, before the corresponding thread is actually started, i.e. the command Producer::start should have triggered the start, a.k. unlocking of the mutex, however it is not yet picked up by the thread's run method (Producer::run), (NB: I'm not 100% sure of this!). I'm a bit lost here, hopefully somebody can provide some help.

Aucun commentaire:

Enregistrer un commentaire