Нужно ли проверять ложные пробуждения по условным переменным в этой ситуации чтения-записи? - PullRequest
0 голосов
/ 31 октября 2018

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

Итак, мы делаем что-то вроде этого:

 condition_variable .wait (lock, [&] () {return jobs.size() > 0;});
 execute_job (jobs.pop());

Хорошо, но теперь я в своей функции execute_job. У меня есть вторая переменная условия, которая предлагает довольно запланированный доступ к другим потокам чтения

 void execute_job (job & j)
 {
     current_job = & j;

     while (! j.finished ())
     {
         std::unique_lock <std::mutex> lock (reader_mutex);

         j.do_some_of_it();

         reader_condition.notify_one ();
         reader_condition.wait (lock);
     }

     current_job = nullptr;
 }

Читательская ветка будет время от времени (ожидая не долго ждать)

 void inspect_current_job ()
 {
      std::unique_lock <std::mutex> lock (reader_mutex);

      if (current_job)
          inspect (current_job);

      reader_condition.notify_one ();
 }

Является ли этот код безопасным и правильным, особенно если учесть, что я не проверяю ложные пробуждения на reader_mutex?

1 Ответ

0 голосов
/ 31 октября 2018

Не создавайте код таким образом, чтобы мьютекс удерживался все время или большую часть времени. Вот что вызывает у вас проблемы с честностью.

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

class prio_lock
{
    private:

    std::mutex m;
    std::condition_variable cv;
    int locked = 0;
    int waiting_priority = 0;

    public:

    void lock(bool priority)
    {
        std::unique_lock<std::mutex> lk(m);

        // wait for all prior priority locks
        while (waiting_priority > 0)
            cv.wait(lk);

        if (priority)
            ++waiting_priority;

        // wait for lock to be unlocked
        while (locked != 0)
            cv.wait(lk);

        if (priority)
            --waiting_priority;

        // take the lock
        locked = 1;
    }

    void unlock()
    {
        std::unique_lock<std::mutex> lk(m);
        locked = 0;
        cv.notify_all();
    }
};

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

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

...