我们有一个实时操作系统,它通过所谓的邮箱提供任务间通信。邮箱由 RTKMailbox 类型的句柄描述。该 API 如下所示:
int RTKPut(RTKMailbox h, const void* data);
int RTKGet(RTKMailbox h, void* data);
邮箱知道数据的大小。数据传输可以被认为是从发送方到接收方的内存。
假设我有一个生产者任务和一个消费者任务;通过该系统发送shared_ptr是个好主意吗?
由于邮箱不知道shared_ptr我的想法是将shared_ptr包装在传输结构中。
代码可能如下所示:
class MyData {
//...
};
struct TransportWrapper {
void BeforePut();
void AfterGet();
std::shared_ptr<MyData> Data;
TransportWrapper() {}
TransportWrapper(std::shared_ptr<MyData>& _data) : Data(_data)
{}
};
void Send(RTKMailbox mbHandle, std::shared_ptr<MyData>& data)
{
TransportWrapper wrap(data);
wrap.BeforePut();
RTKPut(mbHandle, &wrap);
}
std::shared_ptr<MyData> Receive(RTKMailbox mbHandle)
{
TransportWrapper wrap;
RTKGet(mbHandle, &wrap);
wrap.AfterGet();
return wrap.Data;
}
在 BeforePut 中我必须做什么才能防止在包装器的生命周期结束时删除shared_ptr?
在 AfterGet 中我必须执行哪些操作才能将shared_ptr恢复到放置之前的状态?
问候安德烈亚斯
您的示例代码不起作用,您不能只memcpy
shared_ptr
,因为这样做只是复制它包含的指针,它不会创建shared_ptr
的新副本并增加引用计数。不能将memcpy
用于具有非平凡构造函数或析构函数的对象。
假设发送方和接收方共享一个地址空间(因为否则这几乎不可能通过邮箱 API 完成,您需要共享内存),您需要在发送方端增加shared_ptr
的引用计数,以确保发送方不会删除其对拥有对象的最后一个引用并在接收方收到它之前将其删除。然后接收器必须减少参考计数,因此他们需要协调。
如果传递到邮箱是异步的(即发送方在传递完成并且接收方收到数据之前不会阻塞),则无法使用 Send
函数中的局部变量执行此操作,因为这些变量将在 RTKPut 调用返回后立即超出范围,这将在接收方获取引用计数之前减少引用计数(并可能销毁数据)。
解决此问题的最简单方法是在堆上创建一个新shared_ptr
并传输其地址。
void Send(RTKMailbox mbHandle, const std::shared_ptr<MyData>& data)
{
std::shared_ptr<MyData>* p = new std::shared_ptr<MyData>(data);
if (RTKPut(mbHandle, &p) != success)
{
delete p;
// deal with it
}
}
std::shared_ptr<MyData> Receive(RTKMailbox mbHandle)
{
std::shared_ptr<MyData>* p = nullptr;
if (RTKGet(mbHandle, &p) == success)
{
auto sp = *p;
delete p;
return sp;
}
// else deal with it
}
这假设如果RTKPut
成功返回,则传递不会失败,否则您将泄漏在堆上创建shared_ptr
,并且永远不会删除它拥有的对象。