将 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