问题描述
我正在使用 Arduio/ESP32,而且我对 FreeRTOS 非常陌生。我想有一个任务,专门负责在串口上打印文本,其他任务可以推送消息。 因此,我决定使用具有 10 个项目容量的字符数组(或 std::string)队列。 但我不确定队列存储分配如何处理不同长度的元素!
您能否启发我应该如何创建和使用队列以及我应该考虑哪些因素?
解决方法
TL;DR:创建一个指向 std::string
的指针队列,并在任一侧处理 new
/delete
。确保生产者和消费者都使用共享内存空间。
在像 FreeRTOS std::string
这样的“原始”内存 API 中使用 Queue
的问题实际上不是对象大小的问题。实际上,std::string
对象大小是固定的,与对象存储的字符数组的大小无关。不相信我?尝试在您自己的机器上编译并运行这个简单的程序:
#include <iostream>
int main(int argc,char **argv)
{
std::string str1 = "short";
std::string str2 = "very very very very very very long";
std::cout << "str1 length = " << sizeof(str1) << "\n";
std::cout << "str2 length = " << sizeof(str2) << "\n";
return 0;
}
你会得到这样的东西(实际大小会因你的平台而异):
str1 length = 24
str2 length = 24
请注意,如果使用 str1.size()
,结果会有所不同。
这样做的原因是像 std::string
这样的标准库容器通常将它们的内容存储在 malloc 的块中,因此 std::string
对象本身只会存储一个指向包含字符数据的数组的指针在字符串中。在这种情况下,sizeof()
从编译器的角度告诉您对象的大小,这将是指针和其他固定元数据(即结构的字段)的大小。 .size()
方法告诉您来自实现的字符串的大小,其中将包括对分配的字符数组的字符串长度计算。
您不能只将 std::string
对象复制到 xQueueSend()
变体中的原因是生命周期管理问题。 FreeRTOS API 通过原始 *Send
执行 *Receive
和 memcpy
。标准库容器通常不是 POD 类型,这意味着它们必须通过专用的复制构造函数进行复制。如果您不这样做,您可能会使对象的某些内部状态无效,除非您真的知道自己在做什么。
因此,使这项工作最简单的方法如下所示:
- 在
xQueueCreate()
上,将对象大小设置为sizeof(std::string *)
:
xQueue = xQueueCreate(NUM_OBJECTS,sizeof(std::string *));
- 在
xQueueSend()
上,通过运算符std::string
创建一个new
,并将地址传递给要复制的指针。不要删除对象。
std::string *pStr = new std::string("hello world!");
xQueueSend(xQueue,&pStr,(TickType_t)0);
- 在
xQueueReceive()
上,复制出指针。用这个指针做你需要做的事情,然后delete
它。
std::string *pStr = NULL;
xQueueReceive(xQueue,(TickType_t)10);
// do stuff with pStr
delete pStr;
,
嗨,在这个例子中,我将使用结构在队列中发送一个字符串数据:
首先我们创建结构:
typedef struct log_payload_t
{
uint8_t message[256];
} log_payload_t;
创建队列:
xQueueHandle log_queue;
int main(int argc,char **argv)
{
log_queue = xQueueCreate(10,sizeof(log_payload_t));
}
将字符串发送到队列:
log_payload_t payload;
memset(payload.message,256);
memcpy(payload.message,"Example text",strlen(str));
if (xQueueSend(log_queue,&payload,0))
{
printf("added message to log queue \n");
}
else
{
printf("failed to add message to log queue\n");
}
从队列接收:
log_payload_t payload;
if (xQueueReceive(log_queue,0))
{
printf("Log Queue Data %s \n",payload.message);
}
else
{
vTaskDelay(10);
}
,
在我看来,使用固定大小缓冲区的容器可能不太理想。向/从队列发送/接收的每个操作都将涉及整个缓冲区的完整副本。它还需要更大且精心挑选的堆栈大小。
freeRTOS message buffers 在我看来非常方便,可以解决您将几乎任意大小的数据块从一项任务发送到另一项任务的需求。