彻底理解FreeRTOS中的队列(Queue)

这篇具有很好参考价值的文章主要介绍了彻底理解FreeRTOS中的队列(Queue)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

“队列”(Queue)提供了任务与任务之间通信的机制。在这样的场景:一个或多个其他的任务产生数据,主任务要依次处理数据,队列就显得非常有用了。

参考资料:《Mastering the FreeRTOS Real Time Kernel》-Chapter 4 Queue Management

FreeRTOS全解析-5.队列(Queue)

目录

1.队列的特征

1.1数据存储

1.2读取队列时阻塞

1.3写入队列时阻塞

1.4在多个队列上阻塞

2.使用队列

2.1创建队列

2.2往队列发送数据

2.3从队列接收数据

2.4查询队列中的项数

2.5例子

3.任务接收不同数据

4.当数据非常大,或者大小不定时

4.1用队列传输大数据

4.2用队列传输大小不定数据

5.从多个队列中接收数据


1.队列的特征

1.1数据存储

队列可以容纳有限数量的固定大小的数据项。一个队列可以容纳的最大项目数称为它的“长度”。每个数据项的长度和大小都在创建队列时设置。

队列通常用作先进先出(FIFO)缓冲区,其中数据被写入队列的末尾(尾部),并从队列的前端(头部)删除。

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

如图,创建一个可以包含五个整数的队列,刚开始是空的。

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

TaskA往队列尾部写入数据,因为队列是空的,尾部其实也就是头部。

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

TaskA继续往队列尾部写入数据,这次的数据是20,排在第一个数据10后面。

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

TaskB从队列头部读取数据,也就是读到了Task第一次写入的数:10.

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

TaskB把10取走后,第二个数据20就变成了当前队列里的第一个数据。

有两种方式可以实现队列:

1.按复制,复制队列是指将发送到队列的数据复制一份到队列中。

2. 按引用,引用队列意味着队列中只保存指向发送到队列的数据的指针,而不是数据本身。

FreeRTOS使用复制队列的方法。通过复制排队比通过引用排队更强大,使用起来更简单,因为:

1.栈中的变量可以直接发送到队列,即使栈变量会在声明它的函数退出后将不存在。

2.不用预先为数据分配空间。

3.发送任务发送完数据后可以立即重用这个变量。

4.发送任务和接收任务是完全分离的——应用程序设计人员不需要关心哪个任务“拥有”数据,或者哪个任务负责发布数据。

5.复制队列可以同时使用引用功能。例如,当队列中的数据的大小使得将数据复制到队列中不切实际时,则可以将指向数据的指针复制到队列中。

6.RTOS完全负责分配用于存储数据的内存。

7.在内存保护的系统中,任务可以访问的RAM将受到限制。在这种情况下,只有当发送和接收任务都可以访问存储数据的RAM时,才可以使用引用队列。复制队列没有这种限制;内核总是以完全权限运行,允许使用队列跨内存保护边界传递数据。

1.2读取队列时阻塞

当一个任务试图从队列中读取数据时,它可以指定一个“阻塞”时间。

队列为空的时候无法读取,这个时候就会阻塞,当另一个任务或中断将数据放入队列中时,处于阻塞态的任务将自动移动到就绪态。如果指定的阻塞时间到了,还是没有数据,任务也将自动移到就绪态。

可能存在多个任务读取队列,因此单个队列上可能阻塞了多个等待数据的任务。在这种情况下,当有数据时,只有一个任务将被解除阻塞。被解除阻塞的任务将始终是等待数据的任务中优先级最高的那个任务。如果阻塞的任务具有相同的优先级,则等待数据时间最长的任务将被解除阻塞。

1.3写入队列时阻塞

就像从队列中读取一样,任务在写入队列时可以指定阻塞时间。在这种情况下,发生阻塞是因为队列是满的,没有可以写入的空间。

可能有多个任务试图写入队列,因此一个队列上可能阻塞了多个等待完成发送(写入)操作的任务。在这种情况下,当队列有空间时,只有一个任务将被解除阻塞。被解除阻塞的任务将始终是等待空间的任务中最高优先级的任务。如果阻塞的任务具有相同的优先级,则等待空间时间最长的任务将被解除阻塞。

1.4在多个队列上阻塞

可以将多个队列编组成集合,允许任务进入阻塞状态以等待在集合中的任何一个队列可以使用,这个后面讲。

2.使用队列

2.1创建队列

在使用队列之前,必须用xQueueCreate() 函数创建一个队列并返回一个QueueHandle_t。这个类型的变量被称作队列句柄,用于使用队列。

FreeRTOS V9.0.0还包括xQueueCreateStatic()函数,它在编译时静态分配创建队列所需的内存:FreeRTOS在创建队列时从FreeRTOS堆中分配RAM。RAM用于保存队列数据结构和队列中包含的项。如果没有足够的堆RAM供创建的队列使用,xQueueCreate()将返回NULL。

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,                            UBaseType_t uxitemsize);
参数 作用
uxQueueLength 创建的队列可以容纳的最大项数。
uxitemsize 队列中的单个数据项的字节大小。
返回值 如果返回NULL,则不能创建队列,因为没有足够的堆内存供FreeRTOS分配队列数据结构和存储区域。返回非NULL值表示队列已成功创建。返回值应该存储为所创建队列的句柄。

创建队列后,可以使用xQueueReset() API函数将队列返回到原始的空状态。

2.2往队列发送数据

xQueueSendToBack()用于将数据发送到队列的后面(尾部),xQueueSendToFront()用于将数据发送到队列的前面(头部)。还有个函数xQueueSend()等价于xQueueSendToBack(),他们完全一样。

注意:不要在中断服务程序中调用xQueueSendToFront()或xQueueSendToBack()。在中断中使用时用xQueueSendToFrontFromISR()和xQueueSendToBackFromISR()来代替它们。在讲中断时细讲。

BaseType_t xQueueSendToFront( QueueHandle_t xQueue,                              const void * pvItemToQueue,                              TickType_t xTicksToWait );BaseType_t xQueueSendToBack( QueueHandle_t xQueue,                             const void * pvItemToQueue,                             TickType_t xTicksToWait );
参数 作用
xQueue 数据被发送(写入)到的队列的句柄。
pvItemToQueue 指向要复制到队列中的数据的指针。
xTicksToWait

如果队列已满,任务进入阻塞态以等待队列有空间的时间(等多久)。

(阻塞时间以Tick周期指定,因此它表示的绝对时间依赖于Tick频率。宏pdMS_TO_TICKS()可用于将以毫秒为单位指定的时间转换为以tick为单位指定的时间。)

如果xTicksToWait为0且队列已满,xQueueSendToFront()和xQueueSendToBack()都将立即返回。

设置xTicksToWait为宏portMAX_DELAY将导致任务无限期地等待(没有超时),前提是在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend设置为1。

返回值

有两个可能的返回值:

1.pdPASS

只有当数据成功发送到队列时才返回pdPASS。

2. errQUEUE_FULL

如果由于队列已满而无法将数据写入队列,则返回errQUEUE_FULL。

2.3从队列接收数据

xQueueReceive()用于从队列中接收(读取)一个项。队列里的项(数据)被任务给接收走后,队列就会把这个项(数据)删除。

注意:不要在中断服务程序中调用xQueueReceive()。中断安全的xQueueReceiveFromISR() 函数在讲中断时细讲。

BaseType_t xQueueReceive( QueueHandle_t xQueue,                          void * const pvBuffer,                          TickType_t xTicksToWait );
参数 作用
xQueue 接收(读取)数据的队列句柄。
pvBuffer 指向接收到的数据将被复制到其中的内存的指针。就是你要复制到哪里去。
xTicksToWait

队列为空时,任务阻塞以等待队列中有数据的时间。等它有数据,等多久。

(阻塞时间以Tick周期指定,因此它表示的绝对时间依赖于Tick频率。宏pdMS_TO_TICKS()可用于将以毫秒为单位指定的时间转换为以tick为单位指定的时间。)

如果xTicksToWait为零,那么如果队列已经为空,xQueueReceive()将立即返回。

将xTicksToWait设置为portMAX_DELAY将导致任务无限期地等待(没有超时)。(要在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend设置为1)

返回值

有两个可能的返回值:

1.pdPASS

只有成功从队列中读取数据时才返回pdPASS。

2. errQUEUE_EMPTY

如果由于队列已经为空而无法从队列中读取数据,则返回errQUEUE_EMPTY。

2.4查询队列中的项数

uxQueueMessagesWaiting()用于查询当前在队列中的项的数量。

注意:永远不要从中断服务例程中调用uxQueueMessagesWaiting()。应该使用中断安全的uxQueueMessagesWaitingFromISR()来代替它。

UBaseType_t uxQueueMessagesWaiting(QueueHandle_t xQueue);

2.5例子

static void vSenderTask( void *pvParameters )  int32_t lValueToSend;  BaseType_t xStatus;  lValueToSend = ( int32_t ) pvParameters;    for( ;; ) {    xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );    if( xStatus != pdPASS ) {      vPrintString( "Could not send to the queue.\r\n" );    }  }}static void vReceiverTask( void *pvParameters ){  int32_t lReceivedValue;  BaseType_t xStatus;  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );  for( ;; ) {    if( uxQueueMessagesWaiting( xQueue ) != 0 ) {      vPrintString( "Queue should have been empty!\r\n" );    }    xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );    if( xStatus == pdPASS ) {      vPrintStringAndNumber( "Received = ", lReceivedValue );    } else {        vPrintString( "Could not receive from the queue.\r\n" );      }  }}QueueHandle_t xQueue;int main( void ){  xQueue = xQueueCreate( 5, sizeof( int32_t ) );  if( xQueue != NULL ) {      xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );      xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );      xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );      vTaskStartScheduler();  } else {    }  for( ;; );}

这个例子创建了3个任务Sender1:一直往队列发送100,Sender2:一直往队列发送200,Receiver从队列接收数据。

由于接收任务优先级高,所以一旦队列中有数据,接收任务就会启动,所以队列里最多只有一个数据。两个发送任务优先级是一样的,所以会轮着发送。结果是这样的:

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

3.任务接收不同数据

在FreeRTOS设计中,一个任务从多个源接收数据是很常见的。接收任务需要知道数据来自哪里,以确定数据应该如何处理。

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

如上图,把数据打包成数据结构Data_t。结构成员有两个,一个为枚举类型表示不同的数据来源,另一个为数据值。

CAN总线任务发送到队列里的Data_t型数据中的eDataID为eMotorSpeed表示这是电机转速。

HMI任务发送到队列里的Data_t型数据中的eDataID为eSpeedSetPiont表示这是新设定的点的值。

像这样封装出数据结构,就可以给你的主任务足够的信息,剩下的就是在主任务中编写分辨功能了。

例子:

typedef enum{  eSender1,  eSender2} DataSource_t;typedef struct{  uint8_t ucValue;  DataSource_t eDataSource;} Data_t;static const Data_t xStructsToSend[ 2 ] ={  { 100, eSender1 }, /* Used by Sender1. */  { 200, eSender2 } /* Used by Sender2. */};static void vSenderTask( void *pvParameters ){  BaseType_t xStatus;  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );  for( ;; ) {    xStatus = xQueueSendToBack( xQueue, pvParameters, xTicksToWait );    if( xStatus != pdPASS ) {      vPrintString( "Could not send to the queue.\r\n" );    }  }}static void vReceiverTask( void *pvParameters ){  Data_t xReceivedStructure;  BaseType_t xStatus;  for( ;; ) {    if( uxQueueMessagesWaiting( xQueue ) != 3 )      vPrintString( "Queue should have been full!\r\n" );    xStatus = xQueueReceive( xQueue, &xReceivedStructure, 0 );    if( xStatus == pdPASS ) {      if( xReceivedStructure.eDataSource == eSender1 ) {          vPrintStringAndNumber( "From Sender 1 = ", xReceivedStructure.ucValue );      } else {        vPrintStringAndNumber( "From Sender 2 = ", xReceivedStructure.ucValue );      }    } else {        vPrintString( "Could not receive from the queue.\r\n" );    }  }}int main( void ){  xQueue = xQueueCreate( 3, sizeof( Data_t ) );  if( xQueue != NULL ) {    xTaskCreate( vSenderTask, "Sender1", 1000, &( xStructsToSend[ 0 ] ), 2, NULL );    xTaskCreate( vSenderTask, "Sender2", 1000, &( xStructsToSend[ 1 ] ), 2, NULL );    xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );    vTaskStartScheduler();  } else {  }  for( ;; );}

主函数创建了元素个数为3的队列,元素的类型为Data_t,定义了两个发送任务,发送数据不同,发送的数据中标识了不同的源。

这个例子和前面的例子还有一点不同:发送任务的优先级高于接收任务,也就是说一旦可以发送,就会抢占接收任务,因此队列不会存在空位,总是满的。

freertos queue,FreeRTOS,单片机,嵌入式,FreeRTOS

为什么Sender1接连发送了4个数据?

因为在这个例子里,任务很简单,数据发送特别快,在一个tick内就可以发好几次,队列没有满,tick时间没有到,就不会发生任务切换。

Sender1一直发直到队列满,Sender1阻塞,Sender2尝试发送,但是队列已经满,Sender2也进入阻塞。直到接收任务Receiver接收走一个数据,Sender1和2才有机会解除阻塞,但是Sender1和2优先级相同,就看谁阻塞久,就先启动谁,所以又启动Sender1,这就导致了接连有4个Sender1。

Receiver又接收走一个数据,这个时候就是Sender2阻塞更久了,所以Sender2就开始发送,如此往复。

4.当数据非常大,或者大小不定时

4.1用队列传输大数据

如果存储在队列中的数据很大,那么最好使用队列来传输指向数据的指针,而不是逐个字节地将数据本身复制到队列中或复制出队列。传输指针在处理时间和创建队列所需的RAM量方面更加高效。但是在对指针进行排队时,必须非常小心,以确保:

1. 指针指向的RAM空间的所有者必需清晰

当通过指针在两个任务之间共享内存时,必须确保两者不会同时修改内存内容,也不会采取可能导致内存内容无效或不一致的任何其他操作。理想情况下,在指针进入队列前,只有发送任务被允许访问指针指向的内存。在指针从队列出去后,只有接收任务可以访问指针指向的内存

2. 指针所指向的RAM要保持有效。

如果所指向的内存是动态分配的,或者是从内存池中获得的的预分配缓冲区,则应该有一个任务负责释放内存。在释放内存之后,任何任务都不应该尝试访问内存。

指针绝不能用来访问已分配到任务栈上的数据。在栈帧改变后,数据将无效。

例子:

QueueHandle_t xPointerQueue;xPointerQueue = xQueueCreate( 5, sizeof( char * ) );void vStringSendingTask( void *pvParameters ){  char *pcStringToSend;  const size_t xMaxStringLength = 50;  BaseType_t xStringNumber = 0;  for( ;; ) {    pcStringToSend = ( char * ) prvGetBuffer( xMaxStringLength );    snprintf( pcStringToSend, xMaxStringLength, "String number %d\r\n", xStringNumber );    xStringNumber++;    xQueueSend( xPointerQueue,&pcStringToSend, portMAX_DELAY );  }}void vStringReceivingTask( void *pvParameters ){    char *pcReceivedString;    for( ;; ) {      xQueueReceive( xPointerQueue, &pcReceivedString,portMAX_DELAY );      vPrintString( pcReceivedString );      prvReleaseBuffer( pcReceivedString );    }}

创建了一个最多可以容纳5个指针的队列。

在发送任务vStringSendingTask中分配一个缓冲区,向缓冲区写入一个字符串,然后向队列发送一个指向缓冲区的指针。

在接收任务vStringReceivingTask中从队列接收一个指向缓冲区的指针,然后打印缓冲区中包含的字符串,打印完就释放缓冲区。

4.2用队列传输大小不定数据

前面的部分演示了两种设计模式:向队列发送结构,和向队列发送指针。

把这两个结合起来,任务可以使用单个队列从任何数据源接收任何数据类型。

FreeRTOS+TCP TCP/IP栈的实现就是这样的一个真实例子:

TCP/IP栈在自己的任务中运行,必须处理来自许多不同源的事件。不同的事件类型与不同类型和长度的数据相关联。所有发生在TCP/IP任务之外的事件都由IPStackEvent_t类型的结构描述,并以队列的形式发送给TCP/IP任务。

IPStackEvent_t结构体的pvData成员是一个指针,可以用来直接保存一个值,也可以指向一个缓冲区。

typedef enum{  eNetworkDownEvent = 0,   eNetworkRxEvent,   eTCPAcceptEvent, } eIPEvent_t;typedef struct IP_TASK_COMMANDS{  eIPEvent_t eEventType;  void *pvData;} IPStackEvent_t;

上面代码中的三种eIPEvent_t为:

eNetworkRxEvent:从网络收到一个数据包。从网络接收到的数据以结构体IPStackEvent_t的形式发送到TCP/IP任务。IPStackEvent_t的eEventType成员被设置为eNetworkRxEvent,IPStackEvent_t的pvData成员用于指向包含接收数据的缓冲区。伪代码如下:

void vSendRxDataToTheTCPTask( NetworkBufferDescriptor_t *pxRxedData ){  IPStackEvent_t xEventStruct;  xEventStruct.eEventType = eNetworkRxEvent;  xEventStruct.pvData = ( void * ) pxRxedData;  xSendEventStructToIPTask( &xEventStruct );}

eTCPAcceptEvent:一个套接字正接受或等待来自客户端的连接。任务以结构体IPStackEvent_t的形式发送接收事件到TCP/IP任务。IPStackEvent_t的eEventType成员被设置为eTCPAcceptEvent,IPStackEvent_t的pvData成员被设置为接受连接的套接字的句柄。伪代码如下:

void vSendAcceptRequestToTheTCPTask( Socket_t xSocket ){  IPStackEvent_t xEventStruct;  xEventStruct.eEventType = eTCPAcceptEvent;  xEventStruct.pvData = ( void * ) xSocket;  xSendEventStructToIPTask( &xEventStruct );}

eNetworkDownEvent:网络需要连接或重新连接也就是断网事件。网络接口以结构体IPStackEvent_t的形式发送断网事件到TCP/IP任务。IPStackEvent_t的eEventType成员被设置为eNetworkDownEvent。断网事件与任何数据都没有关联,因此该结构的pvData成员没有被使用。伪代码如下;

void vSendNetworkDownEventToTheTCPTask( Socket_t xSocket ){  IPStackEvent_t xEventStruct;  xEventStruct.eEventType = eNetworkDownEvent;  xEventStruct.pvData = NULL;   xSendEventStructToIPTask( &xEventStruct );}

处理事件代码如下:

IPStackEvent_t xReceivedEvent;xReceivedEvent.eEventType = eNoEvent;xQueueReceive( xNetworkEventQueue, &xReceivedEvent, xNextIPSleep );switch( xReceivedEvent.eEventType ){  case eNetworkDownEvent :    prvProcessNetworkDownEvent();    break;  case eNetworkRxEvent:    prvHandleEthernetPacket( ( NetworkBufferDescriptor_t * )( xReceivedEvent.pvData ) );    break;  case eTCPAcceptEvent:    xSocket = ( FreeRTOS_Socket_t * ) ( xReceivedEvent.pvData );    xTCPCheckNewClient( pxSocket );    break;}

根据IPStackEvent_t的eEventType成员对IPStackEvent_t的pvData成员进行不同的处理。

5.从多个队列中接收数据

上一节说明了如何用一个队列来接收不同大小、不同含义以及来自不同来源的数据。但是有时候受到一些因素限制,必须为某些数据源使用单独的队列。例如,集成到设计中的第三方代码中可能存在专用队列。在这种情况下,可以使用“队列集”。

使用队列集,不如使用结构体的单个队列实现相同功能的设计更简洁、效率更低。因此,建议仅在设计限制,必须使用时才使用队列集。

队列集顾名思义就是把几个队列打包成一个集合。

当队列集中的某个队列接收到数据时,接收到数据的队列的句柄会被发送到队列集。当有一个任务读取队列集时,这个队列句柄就会被返回给这个任务。

显然队列句柄承担了上文中的结构体中枚举变量的标识作用。

使用队列集步骤:

1创建队列集。启用队列集功能要将FreeRTOSConfig.h中的configUSE_QUEUE_SETS设置为1。

QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );

2. 向队列集中添加队列。信号量也可以添加到队列集。信号量以后讲。

BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,QueueSetHandle_t xQueueSet );

3.从队列集中读取数据,以确定集合中的哪些队列包含数据。

QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,const TickType_t xTicksToWait );

根据返回的句柄判断是来自哪个队列的数据,就可以实现对不同数据的不同处理了。

队列集基本上用不到,这几个函数就不详解了,看参数类型也知道怎么用。

往期精彩:

FreeRTOS全解析-4.调度器的三种调度算法

FreeRTOS全解析-3.任务(task)

STM32F4移植FreeRTOS

嵌入式C语言几个重点(const、static、voliatile、位运算)

从Linux内核中学习高级C语言宏技巧文章来源地址https://www.toymoban.com/news/detail-778426.html

到了这里,关于彻底理解FreeRTOS中的队列(Queue)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】——栈和队列(stack、queue)及优先队列(priority_queue)的介绍和模拟实现

    今天我们来学习C++stl六大组件的其中一种,容器适配器,stack、queue及priority_queue都是容器适配器。我们循序渐进,接下来让我们先认识一下什么是容器适配器。 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该

    2024年02月08日
    浏览(51)
  • C++-queue头文件-队列常用函数-#include<queue>新手入门

    队列是在一端允许插入,一端允许删除操作的线性表,是一种先进先出(FIFO)的线性表。在front,即队头,允许删除操作,进行出列操作,在rear,即队尾,允许插入操作,进行入列操作。 在VS编译器中添加queue类,建立队列q 建立队列: queueType, Container (数据类型,容器类型)

    2023年04月09日
    浏览(40)
  • 【数据结构】队列-Queue

    ⭐ 作者:小胡_不糊涂 🌱 作者主页:小胡_不糊涂的个人主页 📀 收录专栏:浅谈数据结构 💖 持续更文,关注博主少走弯路,谢谢大家支持 💖 队列: 只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:

    2024年02月08日
    浏览(44)
  • 消息队列(Message Queue)

    目录 一、概念 二、消息队列使用场景 1.应用解耦:将应用进行解耦 具体场景:用户下单后,订单系统需要通知库存系统 2.异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间 具体场景:用户为了使用某个应用,进行注册,

    2024年02月21日
    浏览(53)
  • C# 队列(Queue)

    目录 一、概述 二、基本的用法 1.添加元素 2.取出元素 1)Dequeue 方法 2)Peek 方法 3.判断元素是否存在 4.获取队列的长度 5.遍历队列 6.清空容器 7.Queue 泛型类 三、结束 表示对象的先进先出集合。 队列和其他的数据结构一样,是一种存储容器,它遵循 先进先出 的原则,能够存

    2024年02月12日
    浏览(35)
  • 数据结构:队列Queue详解

    队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。进行插入操作的一端称为 队尾 ,删除操作的一端称 队头 。 入队列 :进行插入操作的一端称为 队尾 。 出队列 :进行删除操作的一端称为 队头 。 在 Java 中, Queue是个接口,底层是通过链表

    2024年02月11日
    浏览(39)
  • 【C++】队列(queue)的使用

    C++ 中, std::queue 容器是一种 先进先出 (First In First Out, FIFO)的数据结构,且有两个出口。 队列(queue)的 结构 如下:类似于生活中的排队买票,最先排队的人位于队伍最前端,后来的人依次从队伍末尾加入队伍。当队首的人买票成功后离开,原先位于第二位的人顶上成为新的队

    2024年02月09日
    浏览(40)
  • Queue 队列的实现与应用

    只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队(Head/Front), 队列可以通过数组和链表两种方式来实现。 队列的基本操作

    2024年02月07日
    浏览(34)
  • 【数据结构】 队列(Queue)与队列的模拟实现

    队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有==先进先出FIFO(FirstIn First Out) ==入队列: 进行插入操作的一端称为 队尾(Tail/Rear) 出队列: 进行删除操作的一端称为 队头(Head/Front) 在Java中, Queue是个接口,底层是通过链表实现

    2024年02月11日
    浏览(50)
  • leetcode分类刷题:队列(Queue)(一、单调队列)

    单调队列,看起来是与单调栈对应起来的一样;但是做题的时候感觉单调队列不像单调栈一样,能根据题意自然形成 单调队列的基本实现 ,感觉单调队列更像是和某个队列对应起来的一样 1、 单调队列的经典题型 :使用双向队列维护窗口,窗口移动的元素增删与队列的先进

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包