我使用ESP-32,需要使用FreeRTOS队列传递std::shared_ptr
。然而,它失去了一个环节。我认为这是一个问题的根源:
#include <iostream>
#include <memory>
#define PRINT_USE_COUNT(p) std::cout << "Use count: " << p.use_count() << std::endl;
extern "C" {
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
}
class testClass {
public:
testClass() {
std::cout << "Class is constructed" << std::endl;
};
virtual ~testClass() {
std::cout << "Class is destructed" << std::endl;
};
};
struct container {
std::shared_ptr<testClass> field;
};
extern "C" void app_main(void) {
auto queue = xQueueCreate(1, sizeof(container));
auto p = std::make_shared<testClass>();
PRINT_USE_COUNT(p); // 1
{
container c;
c.field = p;
PRINT_USE_COUNT(p); // 2
xQueueSendToBack(queue, &c, 0);
PRINT_USE_COUNT(p); // 2
}
PRINT_USE_COUNT(p); // 1 (Ooops!)
{
container c;
assert(xQueueReceive(queue, &c, 0) == pdTRUE);
PRINT_USE_COUNT(c.field); // 1
}
// Class is destructed
std::cout << "Test finished" << std::endl;
vQueueDelete(queue);
}
所以队列中有一个指针,但它不被计算在内!
我如何解决这个问题(如果可能的话,继续使用FreeRTOS队列(?使用std::move
没有帮助。
如果std::is_trivial<T>::value
为true(主要是POD或可复制对象(,则C风格的原始指针队列仅适用于C++shared_ptr。
由于内存中有memcpy和其他纯C操作,引用计数将无法正确处理(因为它是幕后的C代码,并且不调用析构函数等(,因此可能会导致内存泄漏。
没有简单的方法可以绕过这一点,但最好的方法是自己管理内存。
另请参阅此问题:FreeRTOS 中的共享指针和队列
我设法将唯一指针转换为可以通过消息队列发送的原始格式。请参见此处:https://codereview.stackexchange.com/questions/241886/using-unique-ptr-in-freertos.
请注意,我在代码审查中发布了它,因为我不确定是否真的没有内存泄漏,或者这是否可以更干净地实现。我将测试我是否真的可以在我们的项目中使用这个IPC。
我并不总是认为从头开始开发是最好的选择。在大多数情况下,使用经过良好测试的东西可能是最好的选择,尽管它可能需要一些调整才能满足您的需求。
使用该队列,您可以传递容器的动态创建的实例。使用队列将数据从一个任务发送到同一个任务是非常罕见的,就像上面的例子一样。我不太喜欢在嵌入式CPU中使用动态分配,这种开销有时会对性能产生太大影响。
下面是一个正在工作的PoC,其中传递了一个指向新容器实例的指针,而不是原始副本。在这种方法中,接收任务负责释放实例以避免内存泄漏。
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
}
#include <iostream>
#include <memory>
#define PRINT_USE_COUNT(p) std::cout << "Use count: " << p.use_count() << std::endl;
class testClass {
public:
testClass() {
std::cout << "testClass constructed" << std::endl;
}
~testClass() {
std::cout << "testClass destructed" << std::endl;
}
};
class myContainer {
public:
myContainer(std::shared_ptr<testClass> p) {
_p = p;
std::cout << "myContainer constructed" << std::endl;
}
~myContainer() {
std::cout << "myContainer destructed" << std::endl;
}
std::shared_ptr<testClass>& p() {
return _p;
}
private:
std::shared_ptr<testClass> _p;
};
extern "C" void app_main(void) {
std::cout << "Start of test, creating the shared_ptr..." << std::endl;
auto p = std::make_shared<testClass>();
PRINT_USE_COUNT(p);
std::cout << "Creating one container..." << std::endl;
myContainer c(p);
PRINT_USE_COUNT(p);
std::cout << "Creating the queue..." << std::endl;
auto q = xQueueCreate(1, sizeof(myContainer*));
std::cout << "Sending a dynamically created item to the queue..."
<< std::endl;
myContainer *cp = new myContainer(p);
xQueueSendToBack(q, &cp, 0);
PRINT_USE_COUNT(p);
{
myContainer *pc;
xQueueReceive(q, &pc, 0);
PRINT_USE_COUNT(p);
std::cout << "Use count of pc->p() " << pc->p().use_count()
<< std::endl;
std::cout << "Freeing the dynamically created item..." << std::endl;
delete pc;
PRINT_USE_COUNT(p);
}
std::cout << "end of test" << std::endl;
}
这是程序的输出:
开始测试,创建shared_ptr。。。
testClass构建的
使用次数:1
正在创建一个容器。。。
myContainer构建的
使用次数:2
正在创建队列。。。
正在将动态创建的项目发送到队列。。。
myContainer构建的
使用次数:3
使用次数:3
pc的使用计数->p((3
正在释放动态创建的项。。。
myContainer破坏
使用次数:2
测试结束
myContainer破坏
testClass破坏的