Доступ к переменной-члену только в одном потоке, но не в потоке экземпляра класса - PullRequest
0 голосов
/ 12 декабря 2018

Я столкнулся со следующей проблемой:

У меня есть класс RpcExecutor.Этот класс:

  1. отправляет запросы RPC
  2. получает ответы RPC

Я сохраняю все асинхронно (через boost.asio).Каждый раз, когда отправляется запрос, будет вызываться обработчик записи, который изменяет карту:

async_write(...., [this, ...](error_code ec, size_t bytesTransferred) {
                    //
                    pendingRequests_.insert(requestId, move(myPromise));
           };

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

async_read(... [this, ...](error_code ec, size_t bytesTransferred) {
                  ...
                  pendingRequests_.at(requestId).set_value(...);
                  pendingRequests_.erase(requestId);
});

Мой класс выглядит так:

class RpcExecutor {
private:
      std::unique_ptr<std::map<std::uint64_t, MyPromise>> pendingRequests_;
      ...
};

Чтобы убедиться, что инициализация pendingRequests_,чтение и запись pendingRequests_ выполняются в одном и том же потоке (я проверял, что это так), применяются следующие ограничения:

  1. работает один поток asio::run(),который отличается от RpcExecutor instance.
  2. Я инициализирую pendingRequests_ наведенный объект внутри boost::asio::post(myIoContext, ...), что означает, что он инициализируется в том же потоке, где выполняется asio::run.
  3. Обработчик async_read и обработчик async_write выполняются в том же потоке, что и io_context::run, который совпадает с boost::asio::post.

Всего всего:

  • boost::asio::post, async_read обработчик и async_write обработчик выполняются в одном потоке.
  • RpcExecutor экземпляр класса создается в другом потоке.

Результат

  • async_read и обработчик async_write не видят один и тот же адрес памяти для pendingRequests_.

Вопросы

  1. Как мне инициализировать map, который я могу использовать из своего потока обработчиков, учитывая, что экземпляр класса RpcExecutor находится в другом потоке?Даже если я инициализирую указанный элемент в том же потоке, что и asio::context::run через post, обработчики по-прежнему видят разные адреса для объекта.
  2. Нужны ли мне мьютексы любого вида?На самом деле я хочу сделать все операции чтения / записи из одного потока, даже если он не совпадает с потоком экземпляра класса RpcExecutor, который содержит переменную-член pendingTasks_.

1 Ответ

0 голосов
/ 12 декабря 2018

Проблема была очень тонкой:

Я делал это в какой-то строке моего кода:

executor_ = RpcExecutor(socket_);

Затем был создан RpcExecutor, начал слушать входящие сообщения,все в конструкторе.Но после этого этот объект был перемещен в переменную executor_.

Поскольку мои обработчики async_read и async_read захватывали this, а this не был тем же адресом после перемещения, то они былис разными адресами:

  • при чтении входящих сообщений показывался исходный объект this.
  • в исходящих сообщениях был показан уже перемещенный this адрес.

Решение

  1. Сделать RpcExecutor не копируемым и неподвижным для безопасности.
  2. Я также подумал, что было бы неплохо явно назвать RpcExecutor::listenIncomingMessage.
...