将 shared_ptr 与 FreeRTOS 队列一起使用
Using shared_ptr with FreeRTOS queue
我使用 ESP-32,需要使用 FreeRTOS 队列传递 std::shared_ptr
。然而,它松了一个link。我认为这是问题的根源:
#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
没有帮助。
C 风格的原始指针队列仅适用于 C++ shared_ptr 当且仅当 std::is_trivial<T>::value
为真(主要是 POD 或普通可复制对象)。
由于有 memcpy 和其他普通 C 操作操作内存,因此引用计数将无法正确处理(因为它是幕后的 C 代码,它不会调用析构函数等),您最终可能会得到内存泄漏。
没有简单的方法来规避这个问题,但最好的方法是自己管理内存。
也看到这个问题:Shared pointers and queues in 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;
}
程序的输出如下:
Start of test, creating the shared_ptr...
testClass constructed
Use count: 1
Creating one container...
myContainer constructed
Use count: 2
Creating the queue...
Sending a dynamically created item to the queue...
myContainer constructed
Use count: 3
Use count: 3
Use count of pc->p() 3
Freeing the dynamically created item...
myContainer destructed
Use count: 2
end of test
myContainer destructed
testClass destructed
我使用 ESP-32,需要使用 FreeRTOS 队列传递 std::shared_ptr
。然而,它松了一个link。我认为这是问题的根源:
#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
没有帮助。
C 风格的原始指针队列仅适用于 C++ shared_ptr 当且仅当 std::is_trivial<T>::value
为真(主要是 POD 或普通可复制对象)。
由于有 memcpy 和其他普通 C 操作操作内存,因此引用计数将无法正确处理(因为它是幕后的 C 代码,它不会调用析构函数等),您最终可能会得到内存泄漏。
没有简单的方法来规避这个问题,但最好的方法是自己管理内存。
也看到这个问题:Shared pointers and queues in 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;
}
程序的输出如下:
Start of test, creating the shared_ptr...
testClass constructed
Use count: 1
Creating one container...
myContainer constructed
Use count: 2
Creating the queue...
Sending a dynamically created item to the queue...
myContainer constructed
Use count: 3
Use count: 3
Use count of pc->p() 3
Freeing the dynamically created item...
myContainer destructed
Use count: 2
end of test
myContainer destructed
testClass destructed