У меня есть следующая задача, которую я хочу выполнить. Я хочу запустить диспетчер / пул потоков, который должен запускать все потоки и ждать, пока не появится работа. Затем я должен сигнализировать потокам о выполнении работы, и я должен получить сигнал обратно, когда все будет завершено. Я реализовал это ниже, но похоже, что это не работает.

class ThreadManager{
public:
    std::vector<std::thread> threads;
    std::vector<bool> threadCompletes;

    std::mutex sendMtx, recvMtx;
    std::condition_variable sendCv;

    static void thread_worker(int id, ThreadManager* manager){
        while(1){
            cout << "Thread Wait: " << id << endl;
            std::unique_lock<std::mutex> lck(manager->sendMtx);
            manager->sendCv.wait(lck);
            **lck.unlock();** # JUST ADDED THIS
            // DO WORK
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            manager->threadCompletes[id] = true;
            bool done = (std::find(std::begin(manager->threadCompletes), std::end(manager->threadCompletes), false) == std::end(manager->threadCompletes));
            cout << "Job Complete: " << id << endl;
            if(done){
                cout << "All Done" << endl;
                manager->recvMtx.unlock();
            }
        }
    }

    ThreadManager(){

    }

    void addThread(){
        threadCompletes.push_back(false);
        threads.push_back(std::thread(ThreadManager::thread_worker, threads.size(), this));
    }

    void signal(){
        for(auto i : threadCompletes) i = 0;
        recvMtx.lock();
        std::unique_lock<std::mutex> sendLck(sendMtx);
        sendCv.notify_all();
    }

    void wait(){
        recvMtx.lock();
        recvMtx.unlock();
    }

    void join(){
        for (int i = 0; i < threads.size(); ++i) {
            threads[i].join();
        }
    }
};


void test2(){

    ThreadManager manager;
    for(int i=0; i<5; i++){
        manager.addThread();
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));

    manager.signal();
    manager.wait();
    cout << "Complete" << endl;

    manager.join();

}

Что происходит, так это то, что все потоки запускаются и блокируются при вызове ожидания, и когда я сигнализирую об этом, кажется, что каждый поток запускается только впоследствии.

Thread Wait: 0
Thread Wait: 1
Thread Wait: 2
Thread Wait: 3
Thread Wait: 4
Job Complete: 0
Thread Wait: 0
Job Complete: 1 # Done one after the other 500ms
Thread Wait: 1
Job Complete: 2 # Done one after the other 500ms
Thread Wait: 2
Job Complete: 3 # Done one after the other 500ms
Thread Wait: 3
Job Complete: 4 # Done one after the other 500ms
All Done
Thread Wait: 4
Complete

Что я делаю не так

РЕДАКТИРОВАТЬ:

Я добавил флаг разблокировки. Однако теперь, если я сделаю что-то подобное, это в конечном итоге не удастся:

for(int i=0; i<10; i++){
    manager.signal();
    manager.wait();
    cout << "Complete" << endl;
}

cout << "ALL DONE" << endl;

Обычно он запускается до «Завершено» 2 или 3 раза, но никогда не достигает «ВСЕ ВЫПОЛНЕНО». Происходит какое-то другое состояние гонки

-1
raaj 17 Фев 2018 в 22:22

1 ответ

Лучший ответ

Каждый поток блокирует один и тот же мьютекс на протяжении большей части своего жизненного цикла, включая операцию ожидания 500 мс, которая может начаться только после того, как поток получит мьютекс. Это эффективно предотвращает параллелизм и делает бесполезным многопоточность.

Я вообще не понимаю, зачем нужен сон в рабочих потоках, но, если он есть, переместите его за пределы блокировки мьютекса. Сделайте это, разблокировав, как только переменная состояния проснется; он больше не нужен заблокированным; это освободит другие потоки для одновременной работы.

0
Lightness Races in Orbit 17 Фев 2018 в 22:28