我面临以下问题:
我有一个RpcExecutor
课。此类:
- 发送 RPC 请求
- 接收 RPC 响应
我保持一切异步(通过boost.asio)。每次发送请求时,都会调用一个修改映射的写入处理程序:
async_write(...., [this, ...](error_code ec, size_t bytesTransferred) {
//
pendingRequests_.insert(requestId, move(myPromise));
};
我开始在同一个插座里听,async_read_until
,回复来了,可能是乱序的。我还需要修改这个 hanlder 中的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_
的读取和写入是通过同一个线程完成的(我确实检查过是这种情况),我应用了以下限制:
- 有一个
asio::run()
线程正在运行,该线程与RpcExecutor
实例不同。 - 我在
boost::asio::post(myIoContext, ...)
中初始化pendingRequests_
指向的对象,这意味着它是在执行asio::run
的同一线程中初始化的。 async_read
处理程序和async_write
处理程序与io_context::run
在同一线程中执行,与boost::asio::post
是同一线程。
总而言之:
boost::asio::post
,async_read
处理程序和async_write
处理程序在同一线程中执行。RpcExecutor
类实例是在另一个线程中创建的。
结果
async_read
和async_write
处理程序看不到pendingRequests_
的相同内存地址。
问题
- 考虑到类实例在另一个线程中,我应该如何初始化可以从处理程序线程中使用的
map
RpcExecutor
?即使我通过post
在与asio::context::run
相同的线程中初始化指向元素,处理程序仍然会看到对象的不同地址。 - 我需要任何类型的互斥体吗?实际上,我希望从单个线程执行所有读取/写入,即使与保存
pendingTasks_
成员变量RpcExecutor
类实例不是同一线程。
问题非常微妙:
我在代码的某行中执行此操作:
executor_ = RpcExecutor(socket_);
然后创建RpcExecutor
,开始侦听传入的消息,所有这些都在构造函数中。但在那之后,该对象被移动到executor_
变量中。
由于我的async_read
和async_read
处理程序正在捕获this
并且移动后this
地址不同,因此它们显示不同的地址:
- 读取的传入消息显示原始对象
this
。 - 传出邮件显示已移动的
this
地址。
溶液
- 为了安全起见,使
RpcExecutor
不可复制和不可移动。 - 我也认为明确地打电话给
RpcExecutor::listenIncomingMessage
是个好主意。