Время ожидания и бесконечное ожидание для одной и той же переменной состояния? - PullRequest
1 голос
/ 04 февраля 2020

Сценарий:
У меня есть механизм ожидания и сигнала на основе condition_variable . Это работает! Но мне нужно немного больше, чем просто механизм ожидания и сигнала класса c. Я должен быть в состоянии сделать синхронизированное ожидание так же как бесконечное ожидание "на том же самом condition_variable". Поэтому я создал класс-оболочку вокруг condition_variable, который также решает проблему ложного пробуждения. Ниже приведен код для этого:

Код:

// CondVarWrapper.hpp
#pragma once
#include <mutex>
#include <chrono>
#include <condition_variable>

class CondVarWrapper {
public:
    void Signal() {
        std::unique_lock<std::mutex> unique_lock(cv_mutex);
        cond_var_signalled = true;
        timed_out = false;
        unique_lock.unlock();
        cond_var.notify_one();
    }

    bool WaitFor(const std::chrono::seconds timeout) {
        std::unique_lock<std::mutex> unique_lock(cv_mutex);
        timed_out = true;
        cond_var.wait_for(unique_lock, timeout, [this] {
            return cond_var_signalled;
        });
        cond_var_signalled = false;
        return (timed_out == false);
    }

    bool Wait() {
        std::unique_lock<std::mutex> unique_lock(cv_mutex);
        timed_out = true;
        cond_var.wait(unique_lock, [this] {
            return cond_var_signalled;
        });
        cond_var_signalled = false;
        return (timed_out == false);
    }

private:
    bool cond_var_signalled = false;
    bool timed_out  = false;
    std::mutex  cv_mutex;
    std::condition_variable cond_var;
};


// main.cpp
#include "CondVarWrapper.hpp"
#include <iostream>
#include <string>
#include <thread>

int main() {
   CondVarWrapper cond_var_wrapper;

   std::thread my_thread = std::thread([&cond_var_wrapper]{
       std::cout << "Thread started" << std::endl;
        if (cond_var_wrapper.WaitFor(std::chrono::seconds(10))) {
            std::cout << "Thread stopped by signal from main" << std::endl;
        } else {
            std::cout << "ERROR: Thread stopping because of timeout" << std::endl;
        }
    });

   std::this_thread::sleep_for(std::chrono::seconds(3));
   // Uncomment following line to see the timeout working
   cond_var_wrapper.Signal();
   my_thread.join();
}

Вопрос:
Над кодом хорошо, но я думаю, что есть одна проблема? Могу ли я действительно сделать wait, а также wait_for на том же condition_variable? Что, если поток получил cv_mutex, вызвав CondVarWrapper::Wait(), и этот по какой-то причине никогда не вернулся. И затем появляется другой поток, вызывающий CondVarWrapper::WaitFor(std::chrono::seconds(3)), ожидающий возврата, если он не преуспеет в течение 3 секунд. Теперь этот второй поток не сможет вернуться из WaitFor через 3 секунды, не так ли? На самом деле это никогда не вернется. Потому что ожидание condition_variable - это время ожидания, а не блокировка cv_mutex. Я прав или я не прав в этом понимании?

Если я прав выше, мне нужно заменить std::mutex cv_mutex на std::timed_mutex cv_mutex, выполнить timed_wait в CondVarWrapper::WaitFor и сделать бесконечное ожидание CondVarWrapper::Wait? Или есть ли лучшие / более простые способы справиться с этим?

1 Ответ

1 голос
/ 04 февраля 2020

Мьютекс освобождается при вызове std::condition::wait в условной переменной cond_var. Таким образом, когда вы вызываете CondVarWrapper::Wait из одного потока, он освобождает мьютекс при вызове std::condition::wait и зависает там вечно, второй поток все еще может вызвать CondVarWrapper::WaitFor и успешно заблокировать мьютекс cv_mutex.

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