Условие ожидания C ++ - PullRequest
       14

Условие ожидания C ++

0 голосов
/ 17 октября 2019

Предположим, у меня есть программа с рабочим потоком, который возводит в квадрат число из очереди. Проблема заключается в том, что если работа должна быть освещена (выполнение занимает короткое время), работник заканчивает работу и уведомляет основной поток, прежде чем он успеет даже успеть дождаться окончания работы.

Моя простая программа выглядит следующим образом:

#include <atomic>
#include <condition_variable>
#include <queue>
#include <thread>

std::atomic<bool> should_end;

std::condition_variable work_to_do;
std::mutex work_to_do_lock;

std::condition_variable fn_done;
std::mutex fn_done_lock;
std::mutex data_lock;

std::queue<int> work;
std::vector<int> result;

void worker() {
    while(true) {
        if(should_end) return;

        data_lock.lock();
        if(work.size() > 0) {
            int front = work.front();
            work.pop();
            if (work.size() == 0){
                fn_done.notify_one();
            }
            data_lock.unlock();
            result.push_back(front * front);


        } else {
            data_lock.unlock();

            // nothing to do, so we just wait
            std::unique_lock<std::mutex> lck(work_to_do_lock);
            work_to_do.wait(lck);
        }
    }
}


int main() {

    should_end = false;
    std::thread t(worker); // start worker

    data_lock.lock();
    const int N = 10;
    for(int i = 0; i <= N; i++) {
        work.push(i);
    }
    data_lock.unlock();

    work_to_do.notify_one(); // notify the worker that there is work to do
    //if the worker is quick, it signals done here already
    std::unique_lock<std::mutex> lck(fn_done_lock);
    fn_done.wait(lck);

    for(auto elem : result) {
        printf("result = %d \n", elem);
    }

    work_to_do.notify_one(); //notify the worker so we can shut it down
    should_end = true;

    t.join();
    return 0;
}

1 Ответ

1 голос
/ 17 октября 2019

Ваша попытка использовать само уведомление об условной переменной в качестве признака того, что работа выполнена, в корне ошибочна. Во-первых, у std::conditional_variable могут быть ложные пробуждения, поэтому делать это не следует. Вы должны использовать свой размер очереди в качестве фактического условия для завершения работы, проверить и изменить его под тем же мьютексом, защищенным во всех потоках, и использовать одну и ту же блокировку мьютекса для переменной условия. Затем вы можете использовать std::conditional_variable, чтобы дождаться окончания работы, но вы делаете это после того, как проверили размер очереди, и если работа выполняется в данный момент, вы вообще не собираетесь ждать. В противном случае вы проверяете размер очереди в цикле (из-за ложных пробуждений) и ждете, если он все еще не пуст или вы используете std::condition_variable::wait() с предикатом, который имеет цикл внутри.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...