Не удержание блокировки при уведомлении переменной условия повышенной обработки вызывает проблемы - PullRequest
0 голосов
/ 31 января 2020

ОБНОВЛЕНИЕ 21.02.2020: Удержание блокировки во время уведомления на самом деле не помогает. Как я понимаю, переменная условия остается недействительной в общей памяти в результате процесса ожидания.

Итак, у меня есть это приложение, использующее промежуточный процесс boost для совместного использования памяти, и доступ к нему синхронизируется с использованием условия межпроцессного процесса. переменная. Я использую повышение 1.62 на Windows. Я компилирую с использованием Microsoft Windows Build Tools 2015.

Что происходит, когда я завершаю процесс ожидания с помощью Ctrl- C, процесс уведомления застревает в вызове notify.

Вот демонстрационная программа, которая позволяет воспроизвести проблему. Вы должны запустить исполняемый файл один раз без каких-либо аргументов, чтобы запустить процесс ожидания, и еще раз с некоторым аргументом, чтобы запустить процесс уведомления. Тогда убей первый процесс. Иногда вы заметите, что печать останавливается на «Входящем уведомлении».

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>

#include <iostream>

struct shared_data
{
   boost::interprocess::interprocess_mutex mutex;
   boost::interprocess::interprocess_condition condition;

   bool test_bool = false;
};


int main(int argc, char *argv[])
{
    using namespace boost::interprocess;

    if (argc == 1) {
        struct shm_remove
        {
            shm_remove() {
                shared_memory_object::remove("MySharedMemory");
            }
            ~shm_remove() {
                shared_memory_object::remove("MySharedMemory");
            }
        } remover;

        shared_memory_object shm(create_only, "MySharedMemory", read_write);

        shm.truncate(sizeof(shared_data));
        mapped_region region(shm, read_write);
        void* addr = region.get_address();
        shared_data* data = new (addr) shared_data;

        while (true) {
            scoped_lock<interprocess_mutex> lock(data->mutex);
            while (!data->test_bool) {
                data->condition.wait(lock);
            }
            std::cout << "test_bool became true" << std::endl;
            data->test_bool = false;
        }
    }
    else {
        shared_memory_object shm(open_only, "MySharedMemory", read_write);
        mapped_region region(shm, read_write);
        shared_data* data = static_cast<shared_data*>(region.get_address());
        while (true) {
            {
                scoped_lock<interprocess_mutex> lock(data->mutex);
                data->test_bool = true;
            }
            std::cout << "Entering notify" << std::endl;
            data->condition.notify_one();
            std::cout << "Exiting notify" << std::endl;
        }
    }
}

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

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

Теперь, учитывая, что у меня есть решение моей проблемы, мои вопросы:

  1. Требуется ли получить блокировку при уведомлении об ожидаемом и единственно правильном использовании, или это ошибка?
  2. Если это ожидаемое использование, почему ?

1 Ответ

0 голосов
/ 31 января 2020

Вы не должны удерживать блокировку при вызове notify, но в большинстве случаев вы все равно должны это делать, потому что в противном случае некоторые потоки (или в вашем случае процессы) могли пропустить уведомление. Рассмотрим следующий сценарий:

  • процесс 1 получает блокировку и проверяет условие, но имеет приоритет перед вызовом condition.wait
  • процесс 2 вызывает condition.notify_one - но процесс отсутствует чтобы получить уведомление
  • вы уничтожаете процесс 2
  • теперь процесс 1 наконец-то вызывает condition.wait - и ждет бесконечно.

Получив блокировку перед вызовом notify, вы может гарантировать, что другой процесс уже вызвал wait и поэтому не может пропустить уведомление. Это также относится к std::condition_variable, а не только к вашему примеру с процессами.

Есть несколько ситуаций, когда это не может быть проблемой (например, потому что вы не ждете вечно, а только в течение ограниченного времени), но вы должны быть очень осторожны там.

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