在实际应用中,我们会遇到一个任务或者中断服务需要和另一个任务进行消息传递,FreeRTOS提供了队列的机制来完成任务与任务、任务与中断之间的消息传递。

0x01 队列简介

队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。队列中能保存的最大数据项目数量叫做队列的长度。

1. 数据存储

队列提供了FIFO、LIFO的存储缓冲机制,数据发送到队列中会导致数据拷贝,数据拷贝是值传递,在队列中存储的是数据的原始值,而不是原始值的引用(即值传递数据的引用),FreeRTOS中使用队列传递消息的话虽然使用的是数据拷贝,但是也可以使用引用来传递消息,直接往队列中发送指向这个消息的地址指针就可以了。

2. 多任务访问

队列不属于某个特别指定的任务,任何任务都可以向队列中发送消息,提取消息

0x02 队列结构体

队列的类型是Queue_t,定义在queue.c文件中

typedef struct QueueDefinition
{int8_t *pcHead;                    /*< Points to the beginning of the queue storage area. */int8_t *pcTail;                 /*< Points to the byte at the end of the queue storage area.  Once more byte is allocated than necessary to store the queue items, this is used as a marker. */int8_t *pcWriteTo;                /*< Points to the free next place in the storage area. */union                           /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */{int8_t *pcReadFrom;          /*< Points to the last place that a queued item was read from when the structure is used as a queue. */UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */} u;List_t xTasksWaitingToSend;        /*< List of tasks that are blocked waiting to post onto this queue.  Stored in priority order. */List_t xTasksWaitingToReceive;  /*< List of tasks that are blocked waiting to read from this queue.  Stored in priority order. */volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */UBaseType_t uxLength;           /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */UBaseType_t uxItemSize;          /*< The size of each items that the queue will hold. */volatile int8_t cRxLock;      /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */volatile int8_t cTxLock;       /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked.  Set to queueUNLOCKED when the queue is not locked. */#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )uint8_t ucStaticallyAllocated;    /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */#endif#if ( configUSE_QUEUE_SETS == 1 )struct QueueDefinition *pxQueueSetContainer;#endif#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxQueueNumber;uint8_t ucQueueType;#endif} xQUEUE;/* The old xQUEUE name is maintained above then typedefed to the new Queue_t
name below to enable the use of older kernel aware debuggers. */
typedef xQUEUE Queue_t;
  • pcHead:指向队列存储区首地址
  • pcTail:指向队列存储区最后一个字节地址
  • pcWriteTo:指向下一个可以存储的地址
  • pcReadFrom:当用作队列的时候,指向最后一个出队的队列项首地址
  • uxRecursiveCallCount:当用作递归互斥量的时候用来记录递归互斥量被调用的次数
  • xTasksWaitingToSend:等待发送任务列表,那些因为队列满导致入队失败而进入阻塞态的任务就会挂到此列表上
  • xTasksWaitingToReceive:等待接受任务列表,那些因为队列空导致出队失败而进入阻塞态的任务就会挂到此列表上
  • uxMessagesWaiting:队列中当前消息数
  • uxLength:队列中最大允许的消息数量
  • uxItemSize:每个消息最大长度
  • cRxLock:当列表上锁后,统计出队的消息数量
  • cTxLock:当列表上锁后,统计入队的消息数量

0x03 队列创建

队列创建有两种方法:

  1. 方法1:使用xQueueCreate函数动态创建
  2. 方法2:使用xQueueCreateStatic动态创建
1. xQueueCreate

从源码可以看出,使用xQueueCreate,configSUPPORT_DYNAMIC_ALLOCATION 要等于1,支持动态分配内存

 QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,UBaseType_t uxItemSize)
  • uxQueueLength:要创建的队列的队列长度,即最多可以接受多少个消息
  • uxItemSize :队列中每个消息的长度

创建成功返回队列句柄,失败返回NULL

2.xQueueCreateStatic
 QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,uint8_t *pucQueueStorageBuffer,StaticQueue_t *pxQueueBuffer);
  • uxQueueLength:要创建的队列的队列长度,即最多可以接受多少个消息
  • uxItemSize :队列中每个消息的长度
  • pucQueueStorage:指向消息的存储区,是一个uint8_t类型的数组,数组的大小要大于等于(uxQueueLength*uxItemSize )
  • pxQueueBuffer :保存队列结构体

创建成功返回队列句柄,失败返回NULL

0x04 向队列发送消息

1. 函数原型

创建好队列就可以向队列发送消息了,FreeRTOS提供了8个向对列发送消息的API函数。

1.1 xQueueSend、xQueueSendToBack、xQueueSendToToFront
 BaseType_t xQueueSend(QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait);BaseType_t xQueueSendToBack(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);BaseType_t xQueueSendToToFront(QueueHandle_t  xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);
  • QueueHandle_t xQueue:任务句柄,指明要向那个队列发送数据
  • const void *pvItemToQueue:指向要发送的数据,发送的时候会将这个消息拷贝到队列中
  • TickType_t xTicksToWait:阻塞时间,此参数指示队列满的时候任务进入阻塞态等待队列空闲的最大时间,如果是0,队列满会立即返回,当为portMAX_DELAY就会一直等待。

成功返回pdPASS,失败返回errQUEUE_FULL

1.2 xQueueOverwrite
 BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue);
  • QueueHandle_t xQueue:队列句柄,指明要向那个队列发送数据
  • const void * pvItemToQueue:要发送的数据
1.3 xQueueSendFromISR、xQueueSendToBackFromISR、xQueueSendToFrontFromISR
 BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
  • QueueHandle_t xQueue:队列句柄,指明要向那个队列发送数据
  • const void *pvItemToQueue:要发送的数据
  • BaseType_t *pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量由三个函数来设置,用户不用进行设置,用户只需提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。

成功返回pdTURE,失败返回errQUEUE_FULL

0x05 从队列读取消息

从队列中获取消息,FreeRTOS相关API函数如下

1. xQueueReceive、xQueuePeek
BaseType_t xQueueReceive(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait)BaseType_t xQueuePeek(QueueHandle_t xQueue,void *pvBuffer,TickType_t xTicksToWait);
  • QueueHandle_t xQueue:指明读取那个队列的数据
  • void *pvBuffer:读取队列的过程中将读取的数据拷贝到这个缓冲区
  • TickType_t xTicksToWait:阻塞时间,如果为0,队列为空立即返回,当为portMAX_DELAY的话就一直等待,直到队列有数据。
2. xQueueReceiveFromISR
 BaseType_t xQueueReceiveFromISR(QueueHandle_t   xQueue,void *pvBuffer,BaseType_t *pxTaskWoken);BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void *pvBuffer,);
  • QueueHandle_t xQueue:指明读取那个队列的数据
  • void *pvBuffer:读取队列的过程中将读取的数据拷贝到这个缓冲区
  • BaseType_t *pxTaskWoken:标记退出此函数以后是否进行任务切换

返回值:
返回pdTRUE,从队列中读取数据成功,返回pdFALSE,从队列中读取数据失败。

验证

实验设计三个任务,start_task、task1_task、Keyprocess_task这三个任务。
start_task:用来创建其他两个任务
task1_task:读取按键的键值,然后将键值发送到KEY_Queue队列中,并且检查队列的剩余容量等信息
Keyprocess_task:按键处理任务,读取队列Key_Queue中的信息,根据不同的消息值做相应的处理。
实验需要是哪个按键KEY_UP、KEY2、KEY0,不同的按键对应不同的按键值,任务task1_task会将这些值发送到队列Key_Queue中。
实验中创建两个队列Key_Queue和Message_Queue,队列Key_Queue用于传递按键值,队列Message_Queue用于传递串口发送过来的消息。
实验还需要两个中断,一个是串口1接收中断,一个是定时器9中断,他们的作用如下:串口1接收中断,接收串口发送过来的数据,并将接收到的数据发送到队列Message_Queue中,定时器9中断,定时周期为500ms,在定时器中断中读取队列Message_Queue中的消息,并将其显示在LCD上。

start_task代码:

//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建消息队列Key_Queue=xQueueCreate(KEYMSG_Q_NUM,sizeof(u8));        //创建消息Key_QueueMessage_Queue=xQueueCreate(MESSAGE_Q_NUM,USART_REC_LEN); //创建消息Message_Queue,队列项长度是串口接收缓冲区长度//创建TASK1任务xTaskCreate((TaskFunction_t )task1_task,             (const char*    )"task1_task",           (uint16_t       )TASK1_STK_SIZE,        (void*          )NULL,                  (UBaseType_t    )TASK1_TASK_PRIO,        (TaskHandle_t*  )&Task1Task_Handler);   //创建TASK2任务xTaskCreate((TaskFunction_t )Keyprocess_task,     (const char*    )"keyprocess_task",   (uint16_t       )KEYPROCESS_STK_SIZE,(void*          )NULL,(UBaseType_t    )KEYPROCESS_TASK_PRIO,(TaskHandle_t*  )&Keyprocess_Handler); vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
}

在start_task中创建了两个队列,分别是Key_Queue和Message_Queue,Key_Queue用于和按键通信,Message_Queue用于和串口通信。

task1_task代码

//task1任务函数
void task1_task(void *pvParameters)
{u8 key,i=0;BaseType_t err;while(1){key=KEY_Scan(0);            //扫描按键if((Key_Queue!=NULL)&&(key))   //消息队列Key_Queue创建成功,并且按键被按下{err=xQueueSend(Key_Queue,&key,10);if(err==errQUEUE_FULL)   //发送按键值{printf("队列Key_Queue已满,数据发送失败!\r\n");}}i++;if(i%10==0) check_msg_queue();//检Message_Queue队列的容量if(i==50){i=0;LED0=!LED0;}vTaskDelay(10);                           //延时10ms,也就是10个时钟节拍  }
}

task1_task获取按键值,并将按键值发送到Key_Queue中

Keyprocess_task代码:

//Keyprocess_task函数
void Keyprocess_task(void *pvParameters)
{u8 num,key,beepsta=1;while(1){if(Key_Queue!=NULL){if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//请求消息Key_Queue{switch(key){case WKUP_PRES:     //KEY_UP控制LED1LED1=!LED1;break;case KEY1_PRES:     //KEY1控制蜂鸣器beepsta=!beepsta;BEEP=beepsta;break;case KEY0_PRES:        //KEY0刷新LCD背景num++;LCD_Fill(126,111,233,313,lcd_discolor[num%14]);break;}}} vTaskDelay(10);      //延时10ms,也就是10个时钟节拍   }
}

Keyprocess_task获取Key_Queue队列中按键值

整个main.c代码如下:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "sdram.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "string.h"
#include "malloc.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/************************************************ALIENTEK 水星STM32F429开发板 FreeRTOS实验13-1FreeRTOS队列操作实验-HAL库版本技术支持:www.openedv.com淘宝店铺:http://eboard.taobao.com 关注微信公众平台微信号:"正点原子",免费获取STM32资料。广州市星翼电子科技有限公司  作者:正点原子 @ALIENTEK
************************************************///任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小
#define START_STK_SIZE      256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define TASK1_TASK_PRIO     2
//任务堆栈大小
#define TASK1_STK_SIZE      256
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);//任务优先级
#define KEYPROCESS_TASK_PRIO 3
//任务堆栈大小
#define KEYPROCESS_STK_SIZE  256
//任务句柄
TaskHandle_t Keyprocess_Handler;
//任务函数
void Keyprocess_task(void *pvParameters);//按键消息队列的数量
#define KEYMSG_Q_NUM    1       //按键消息队列的数量
#define MESSAGE_Q_NUM   4       //发送数据的消息队列的数量
QueueHandle_t Key_Queue;        //按键值消息队列句柄
QueueHandle_t Message_Queue;    //信息队列句柄//LCD刷屏时使用的颜色
int lcd_discolor[14]={ WHITE, BLACK, BLUE,  BRED,      GRED,  GBLUE, RED,   MAGENTA,            GREEN, CYAN,  YELLOW,BROWN,            BRRED, GRAY };//用于在LCD上显示接收到的队列的消息
//str: 要显示的字符串(接收到的消息)
void disp_str(u8* str)
{LCD_Fill(5,230,110,245,WHITE);                 //先清除显示区域LCD_ShowString(5,230,100,16,16,str);
}//加载主界面
void freertos_load_main_ui(void)
{POINT_COLOR = RED;LCD_ShowString(10,10,200,16,16,"Apollo STM32F4/F7");  LCD_ShowString(10,30,200,16,16,"FreeRTOS Examp 13-1");LCD_ShowString(10,50,200,16,16,"Message Queue");LCD_ShowString(10,70,220,16,16,"KEY_UP:LED1 KEY0:Refresh LCD");LCD_ShowString(10,90,200,16,16,"KEY1:SendMsg KEY2:BEEP");POINT_COLOR = BLACK;LCD_DrawLine(0,107,239,107);     //画线LCD_DrawLine(119,107,119,319);      //画线LCD_DrawRectangle(125,110,234,314); //画矩形POINT_COLOR = RED;LCD_ShowString(0,130,120,16,16,"DATA_Msg Size:");LCD_ShowString(0,170,120,16,16,"DATA_Msg rema:");LCD_ShowString(0,210,100,16,16,"DATA_Msg:");POINT_COLOR = BLUE;
}//查询Message_Queue队列中的总队列数量和剩余队列数量
void check_msg_queue(void)
{u8 *p;u8 msgq_remain_size; //消息队列剩余大小u8 msgq_total_size;     //消息队列总大小taskENTER_CRITICAL();   //进入临界区msgq_remain_size=uxQueueSpacesAvailable(Message_Queue);//得到队列项剩余大小msgq_total_size=uxQueueMessagesWaiting(Message_Queue)+uxQueueSpacesAvailable(Message_Queue);//得到队列总大小,总大小=使用+剩余的。p=mymalloc(SRAMIN,20);    //申请内存sprintf((char*)p,"Total Size:%d",msgq_total_size);  //显示DATA_Msg消息队列总的大小LCD_ShowString(10,150,100,16,16,p);sprintf((char*)p,"Remain Size:%d",msgq_remain_size);   //显示DATA_Msg剩余大小LCD_ShowString(10,190,100,16,16,p);myfree(SRAMIN,p);        //释放内存taskEXIT_CRITICAL();    //退出临界区
}int main(void)
{HAL_Init();                     //初始化HAL库   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhzdelay_init(180);                //初始化延时函数uart_init(115200);              //初始化串口LED_Init();                     //初始化LED KEY_Init();                     //初始化按键BEEP_Init();                  //初始化蜂鸣器SDRAM_Init();                   //初始化SDRAMLCD_Init();                     //初始化LCDTIM9_Init(5000,18000-1);      //初始化定时器9,周期500msmy_mem_init(SRAMIN);            //初始化内部内存池freertos_load_main_ui();        //加载主UI//创建开始任务xTaskCreate((TaskFunction_t )start_task,            //任务函数(const char*    )"start_task",          //任务名称(uint16_t       )START_STK_SIZE,        //任务堆栈大小(void*          )NULL,                  //传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       //任务优先级(TaskHandle_t*  )&StartTask_Handler);   //任务句柄                vTaskStartScheduler();          //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建消息队列Key_Queue=xQueueCreate(KEYMSG_Q_NUM,sizeof(u8));        //创建消息Key_QueueMessage_Queue=xQueueCreate(MESSAGE_Q_NUM,USART_REC_LEN); //创建消息Message_Queue,队列项长度是串口接收缓冲区长度//创建TASK1任务xTaskCreate((TaskFunction_t )task1_task,             (const char*    )"task1_task",           (uint16_t       )TASK1_STK_SIZE,        (void*          )NULL,                  (UBaseType_t    )TASK1_TASK_PRIO,        (TaskHandle_t*  )&Task1Task_Handler);   //创建TASK2任务xTaskCreate((TaskFunction_t )Keyprocess_task,     (const char*    )"keyprocess_task",   (uint16_t       )KEYPROCESS_STK_SIZE,(void*          )NULL,(UBaseType_t    )KEYPROCESS_TASK_PRIO,(TaskHandle_t*  )&Keyprocess_Handler); vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//task1任务函数
void task1_task(void *pvParameters)
{u8 key,i=0;BaseType_t err;while(1){key=KEY_Scan(0);            //扫描按键if((Key_Queue!=NULL)&&(key))   //消息队列Key_Queue创建成功,并且按键被按下{err=xQueueSend(Key_Queue,&key,10);if(err==errQUEUE_FULL)   //发送按键值{printf("队列Key_Queue已满,数据发送失败!\r\n");}}i++;if(i%10==0) check_msg_queue();//检Message_Queue队列的容量if(i==50){i=0;LED0=!LED0;}vTaskDelay(10);                           //延时10ms,也就是10个时钟节拍  }
}//Keyprocess_task函数
void Keyprocess_task(void *pvParameters)
{u8 num,key,beepsta=1;while(1){if(Key_Queue!=NULL){if(xQueueReceive(Key_Queue,&key,portMAX_DELAY))//请求消息Key_Queue{switch(key){case WKUP_PRES:     //KEY_UP控制LED1LED1=!LED1;break;case KEY1_PRES:     //KEY1控制蜂鸣器beepsta=!beepsta;BEEP=beepsta;break;case KEY0_PRES:        //KEY0刷新LCD背景num++;LCD_Fill(126,111,233,313,lcd_discolor[num%14]);break;}}} vTaskDelay(10);      //延时10ms,也就是10个时钟节拍   }
}

FreeRTOS队列相关推荐

  1. FreeRTOS队列集

    任务通信过程中,如果消息类型不同,使用一条队列来实现则有些麻烦. FreeRTOS 提供队列集合,用于对多个队列以及信号量进行"监听",只要其中不管哪一个有消息到来,都可以让任务退 ...

  2. FreeRTOS 队列管理

      基于 FreeRTOS 的应用程序由一组独立的任务构成--每个任务都是具有独立权限的小程序.这些独立的任务之间很可能会通过相互通信以提供有用的系统功能.FreeRTOS 中所有的通信与同步机制都是 ...

  3. FreeRTOS队列原理

    1. 队列 队列是为了任务与任务.任务与中断之间通信而准备的,可以在任务与任务.任务与中断之间传递消息,队列中可以存储有限的.大小固定的数据项.创建队列的时候需要指定数据项目的大小和队列的长度. 1. ...

  4. freeRtos学习笔(4)消息队列

    freeRtos学习笔记 freeRtos消息队列 为什么要用消息队列 消息队列可以在任务与任务间,中断与任务间传递信息.为什么不用全局数组?全局数组也可以传递信息,但是和消息队列相比,消息队列有一下 ...

  5. FreeRTOS笔记篇:第五章 --软件定时

    目录 测试环境如下 概述 读者 软件定时器回调函数 ATimerCallback() 软件计时器的属性和状态 计时器服务-任务 计时器命令-队列 创建和启动软件计时器 xTimerCreate() x ...

  6. 正点原子FreeRTOS(中)

    更多干货推荐可以去牛客网看看,他们现在的IT题库内容很丰富,属于国内做的很好的了,而且是课程+刷题+面经+求职+讨论区分享,一站式求职学习网站,最最最重要的里面的资源全部免费!!!点击进入------ ...

  7. FreeRTOS——静态与动态内存分配

    FreeRTOS 基础系列文章  基本对象   FreeRTOS--任务   FreeRTOS--队列   FreeRTOS--信号量   FreeRTOS--互斥量   FreeRTOS--任务通知 ...

  8. freeRTOS学习(三)

    任务管理 任务功能:任务以C函数的形式实现.唯一特别的地方是它们的原型,它必须返回void并接受一个void指针形参. void ATaskFunction(void *pvParameters); ...

  9. FreeRTOS——软件计时器

    FreeRTOS 基础系列文章  基本对象   FreeRTOS--任务   FreeRTOS--队列   FreeRTOS--信号量   FreeRTOS--互斥量   FreeRTOS--任务通知 ...

最新文章

  1. 【C 语言】数组 ( 数组类型表达 | 定义数组类型 )
  2. Linux系统自启动脚本
  3. 韩国政府计划从Windows 7迁移到Linux
  4. 使用python制作二维码
  5. 解读鸿蒙轻内核的监控器:异常钩子函数
  6. 基于JAVA+SpringMVC+Mybatis+MYSQL的培训中心管理系统
  7. 移动端HTML5音频与视频问题及解决方案
  8. python代码的层次结构_Python的object和type理解及主要对象层次结构
  9. javweb音乐网站_基于jsp的音乐网站-JavaEE实现音乐网站 - java项目源码
  10. Coin3D三维可视化教程1
  11. 国内外IGS数据及产品下载网站
  12. Endnotex7无法保存修改后的格式
  13. MySQL命令执行脚本文件
  14. C#中未将对象引用设置到对象的实例
  15. 程序员的高效工作环境
  16. 关于多元正态分布向量表示的Notation问题
  17. Python程序批量校验统一社会信用代码的校验
  18. 手脱aspack变形壳
  19. 笔记本创建wifi热点
  20. 你肯定用过手机护眼模式, 但你知道怎么将电脑Win10窗口背景色修改为护眼的淡绿色吗?(附带如何备份注册表)

热门文章

  1. SVN版本控制系统学习(中文配置)
  2. 层次和约束:项目中使用vuex的3条优化方案
  3. 【HTML】前端性能优化之CDN和WPO的比较
  4. Nginx 使用try_files遇到的问题
  5. 【BZOJ 1098】办公楼(补图连通块个数,Bfs)
  6. configparser logging
  7. 设置UITableView设置contentsize
  8. java中的codereview
  9. 学习总结:机器学习(一)
  10. c语言最大公约数和最小公倍数_五年级奥数课堂之七:公因数和公倍数