仅在一个线程中访问成员变量,而不是 class 实例的线程
Access member variable in one thread only, but not the thread of the class instance
我面临以下问题:
我有一个RpcExecutor
class。这个class:
- 发送 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 看起来像这样:
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
class 实例在另一个线程中创建。
结果
async_read
和 async_write
处理程序没有看到 pendingRequests_
的相同内存地址。
问题
- 考虑到
RpcExecutor
class 实例在另一个线程中,我应该如何初始化可以从我的处理程序线程使用的 map
?即使我通过 post
在与 asio::context::run
相同的线程中初始化指向的元素,处理程序仍然看到对象的不同地址。
- 我需要任何类型的互斥量吗?实际上,我想从单个线程执行所有 reads/writes,即使与
RpcExecutor
class 实例不同的线程,它保存 pendingTasks_
成员变量。
问题很微妙:
我在代码的某行中这样做:
executor_ = RpcExecutor(socket_);
然后创建了RpcExecutor
,开始监听传入的消息,所有这些都在构造函数中。但是那个对象在那之后被移动到 executor_
变量。
由于我的 async_read
和 async_read
处理程序在移动后捕获 this
和 this
不是同一个地址,因此它们显示不同的地址:
- 读取的传入消息显示原始对象
this
。
- 外发邮件显示已移动的
this
地址。
解决方案
- 为安全起见,使
RpcExecutor
不可复制且不可移动。
- 我还认为显式调用
RpcExecutor::listenIncomingMessage
是个好主意。
我面临以下问题:
我有一个RpcExecutor
class。这个class:
- 发送 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 看起来像这样:
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
class 实例在另一个线程中创建。
结果
async_read
和async_write
处理程序没有看到pendingRequests_
的相同内存地址。
问题
- 考虑到
RpcExecutor
class 实例在另一个线程中,我应该如何初始化可以从我的处理程序线程使用的map
?即使我通过post
在与asio::context::run
相同的线程中初始化指向的元素,处理程序仍然看到对象的不同地址。 - 我需要任何类型的互斥量吗?实际上,我想从单个线程执行所有 reads/writes,即使与
RpcExecutor
class 实例不同的线程,它保存pendingTasks_
成员变量。
问题很微妙:
我在代码的某行中这样做:
executor_ = RpcExecutor(socket_);
然后创建了RpcExecutor
,开始监听传入的消息,所有这些都在构造函数中。但是那个对象在那之后被移动到 executor_
变量。
由于我的 async_read
和 async_read
处理程序在移动后捕获 this
和 this
不是同一个地址,因此它们显示不同的地址:
- 读取的传入消息显示原始对象
this
。 - 外发邮件显示已移动的
this
地址。
解决方案
- 为安全起见,使
RpcExecutor
不可复制且不可移动。 - 我还认为显式调用
RpcExecutor::listenIncomingMessage
是个好主意。