将shared_ptr与FreeRTOS队列一起使用

时间:2018-08-01 11:33:59

标签: c++ smart-pointers freertos

我使用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无济于事。

2 个答案:

答案 0 :(得分:2)

仅当std::is_trivial<T>::value为true(主要是POD或可复制对象)时,C风格的原始指针队列才适用于C ++ shared_ptr。

由于存在memcpy和其他普通的C操作操纵内存,因此引用计数将无法正确处理(因为它是C代码在幕后,并且不会调用析构函数),因此您可能会得到一个内存泄漏。

没有简单的方法可以解决此问题,但是最好的方法是自己管理内存。

也请参见此问题:Shared pointers and queues in FreeRTOS

答案 1 :(得分:0)

我并不总是同意从头开发一些东西是最好的选择。在大多数情况下,使用经过充分测试的东西可能是最佳选择——尽管它可能需要进行一些调整才能满足您的需求。

通过队列,您可以传递动态创建的容器实例。使用队列将数据从一个任务发送到同一个任务是非常罕见的,如果有的话,如上例所示。我不太喜欢在嵌入式 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...

构建的测试类

使用次数:1

正在创建一个容器...

myContainer 已构建

使用次数:2

正在创建队列...

将动态创建的项目发送到队列...

myContainer 已构建

使用次数:3

使用次数:3

使用 pc->p() 3 的次数

释放动态创建的项目...

myContainer 被破坏

使用次数:2

测试结束

myContainer 被破坏

testClass 被破坏