zoukankan      html  css  js  c++  java
  • FreeRTOS-03-其它任务相关函数

    说明:
    本文仅作为学习FreeRTOS的记录文档,作为初学者肯定很多理解不对甚至错误的地方,望网友指正。
    FreeRTOS是一个RTOS(实时操作系统)系统,支持抢占式、合作式和时间片调度。适用于微处理器或小型微处理器的实时应用。
    本文档使用的FreeRTOS版本:FreeRTOS Kernel V10.4.1
    参考文档:《FreeRTOS_Reference_Manual_V10.0.0.pdf》《FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf》《STM32F4 FreeRTOS开发手册_V1.1.pdf》
    参考视频:正点原子FreeRTOS手把手教学-基于STM32_哔哩哔哩_bilibili

    5 其它任务相关函数

    介绍一些任务辅助函数,方便查询任务的相关信息。

    5.1 设置任务优先级

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority );
    

    函数描述:设置任务优先级。

    函数参数:pxTask设置任务优先级的任务句柄。如果任务设置自己的优先级,这个参数可以填为NULL。

    返回值:重新设置的任务优先级值。0表示最低优先级,configMAX_PRIORITIES – 1表示最高优先级。

    5.2 获取任务优先级

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );
    

    函数描述:获取任务优先级。

    函数参数:pxTask查询任务优先级的任务句柄。如果任务查询自己的优先级,这个参数可以填为NULL。

    返回值:查询任务的优先级值。

    测试代码:

    configSTACK_DEPTH_TYPE Task_STACK_SIZE = 5;
    UBaseType_t  Task_Priority = 12;
    
    void task_code(void *para)
    {
        static unsigned int cnt = 0;
    
        for (;;)
        {
            PRINT(" task cnt %u...", cnt);
            cnt++;
            vTaskDelay(1000);
        }
    }
    
    void task_func(void)
    {
        TaskHandle_t xhandle;
        UBaseType_t  uxCreatedPriorty, uxOurPriorty;
        
        if (xTaskCreate(task_code, "demo task", 
            Task_STACK_SIZE, NULL, Task_Priority,
            &xhandle) != pdPASS)
        {
            PRINT("creat task failed!
    ");
        } else
        {
            uxCreatedPriorty = uxTaskPriorityGet(xhandle);
            uxOurPriorty = uxTaskPriorityGet(NULL);
            PRINT("created task priority: %d", uxCreatedPriorty);
            PRINT("our task priority: %d", uxOurPriorty);
    
            vTaskPrioritySet(xhandle, 3);
            uxCreatedPriorty = uxTaskPriorityGet(xhandle);
            uxOurPriorty = uxTaskPriorityGet(NULL);
            PRINT("after changed, created task priority: %d", uxCreatedPriorty);
            PRINT("after changed, our task priority: %d", uxOurPriorty);
        }
    }
    

    默认创建任务优先级为12,然后更改任务优先级为3。

    编译,运行,结果如下:

    $ ./build/freertos-simulator 
    created task priority: 12
    our task priority: 12
    after changed, created task priority: 3
    after changed, our task priority: 3
     task cnt 0...
     task cnt 1...
     ... ...
    

    5.3 获取系统中所有任务的状态

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    UBaseType_t uxTaskGetSystemState( 
        TaskStatus_t * const pxTaskStatusArray,
    	const UBaseType_t uxArraySize,
    	unsigned long * const pulTotalRunTime );
    

    函数描述:获取系统中所有任务的任务状态。TaskStatus_t是一个保存任务状态信息的结构体,结构体中包括任务句柄、任务名称、堆栈、优先级等信息。要使用这个函数需要打开configUSE_TRACE_FACILITY宏。

    函数参数:pxTaskStatusArray:指向TaskStatus_t数据结构数组的首地址,每个任务至少包含一个TaskStatus_t结构体。任务的结构体数目可以使用uxTaskGetNumberOfTasks函数获得。

    uxArraySize:保存任务状态数组的数组的大小。

    pulTotalRunTime:如果configGENERATE_RUN_TIME_STATS配置为1,这个参数保存系统总的运行时间。

    返回值:统计到的任务状态的数目,也就是pxTaskStatusArray数组成员个数,如果uxArraySize参数太小,返回值可能为0。

    TaskStatus_t结构体定义如下:

    typedef struct xTASK_STATUS
    {
        TaskHandle_t xHandle;                       //任务句柄
        const char * pcTaskName;                    //任务名字
        UBaseType_t xTaskNumber;                    //任务编号
        eTaskState eCurrentState;                   //任务当前状态
        UBaseType_t uxCurrentPriority;            	//任务当前优先级  
        UBaseType_t uxBasePriority;                 //任务基础优先级
        uint32_t ulRunTimeCounter;                  //任务运行总时间
        StackType_t * pxStackBase;                  //堆栈基地址
        configSTACK_DEPTH_TYPE usStackHighWaterMark;//从任务创建以来任务堆栈剩余的最小大小,这个值太小说明堆栈有溢出的风险。
    } TaskStatus_t;
    

    5.4 获取系统中单个任务的状态

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    void vTaskGetTaskInfo( TaskHandle_t xTask,
    	TaskStatus_t *pxTaskStatus,
    	BaseType_t xGetFreeStackSpace,
    	eTaskState eState );
    

    函数描述:获取单个任务的任务状态。要使用这个函数需要打开configUSE_TRACE_FACILITY宏。

    函数参数:xTask:任务句柄;pxTaskStatus:存放获取的任务状态信息;

    xGetFreeStackSpace:TaskStatus_t结构中有个成员usStackHighWaterMark存放了任务创建以来任务堆栈剩余的最小大小,但是计算这个值需要一些时间,所以可以通过设置xGetFreeStackSpace值为pdFALSE来跳过这个步骤,当设置为pdTRUE才会检查堆栈剩余的最小大小。

    eState:TaskStatus_t结构中有个成员eCurrentState存放任务的当前运行状态,但是获取任务状态需要花费不少时间,可通过参数eState直接将任务状态赋值给eCurrentState。也可以将eStates设置为eInvalid,那么任务状态信息有函数vTaskGetInfo()函数获取。

    测试代码:

    configSTACK_DEPTH_TYPE Task_STACK_SIZE = 20;
    UBaseType_t  Task_Priority = 12;
    
    void task_code(void *para)
    {
        static unsigned int cnt = 0;
    
        for (;;)
        {
            PRINT(" task cnt %u...", cnt);
            cnt++;
            vTaskDelay(1000);
        }
    }
    
    void task_func(void)
    {
        TaskHandle_t xhandle;
        TaskStatus_t xTaskDetails;
        char *state_str[] = {"running", "ready", "blocked", "suspended", "deleted", "invalid"};
        
        if (xTaskCreate(task_code, "demo task", 
            Task_STACK_SIZE, NULL, Task_Priority,
            &xhandle) != pdPASS)
        {
            PRINT("creat task failed!
    ");
        } else
        {
            vTaskPrioritySet(xhandle, 3);
            vTaskGetTaskInfo(xhandle, &xTaskDetails, pdTRUE, eInvalid);
            PRINT("Task name           : %s", xTaskDetails.pcTaskName);
            PRINT("Task number         : %d", xTaskDetails.xTaskNumber);
            PRINT("Task CurrentState   : %s", state_str[xTaskDetails.eCurrentState]);
            PRINT("Task CurrentPriority: %d", xTaskDetails.uxCurrentPriority);
            PRINT("Task BasePriority   : %d", xTaskDetails.uxBasePriority);
            PRINT("Task RunTimeCounter : %d", xTaskDetails.ulRunTimeCounter);
            PRINT("Task StackBase      : %p", xTaskDetails.pxStackBase);
            PRINT("Task StackHighWaterMark: %u", xTaskDetails.usStackHighWaterMark);
        }
    }
    

    编译,运行,结果如下:

    $ ./build/freertos-simulator 
    Task name           : demo task
    Task number         : 1
    Task CurrentState   : running
    Task CurrentPriority: 3
    Task BasePriority   : 3
    Task RunTimeCounter : 0
    Task StackBase      : 0x2060010
    Task StackHighWaterMark: 15
     task cnt 0...
     task cnt 1...
    

    5.5 获取调度器运行状态

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    BaseType_t xTaskGetSchedulerState( void );
    

    函数描述:获取调度器当前的运行状态。使用这个函数需要将宏INCLUDE_xTaskGetSchedulerState置为1。

    函数参数:

    返回值:INCLUDE_xTaskGetSchedulerState:调度器未启动。调度器启动使用vTaskStartSchedule()函数完成,所以xTaskGetSchedulerState()函数在vTaskStartSchedule()函数之前调用会返回这个值。

    taskSCHEDULER_RUNNING:调度器正在运行。

    taskSCHEDULER_SUSPENDED:调度器被挂起,因为调用了vTaskSuspendAll()函数。

    5.6 获取任务运行状态

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    eTaskState eTaskGetState( TaskHandle_t pxTask );
    

    函数描述:获取任务的运行状态。使用此函数需要将INCLUDE_eTaskGetState宏置为1。

    函数参数:xTask:要获取的任务句柄

    返回值:任务的运行状态,eTaskState是一个枚举变量。

    /* Task states returned by eTaskGetState. */
    typedef enum
    {
        eRunning = 0,     /* A task is querying the state of itself, so must be running. */
        eReady,           /* The task being queried is in a read or pending ready list. */
        eBlocked,         /* The task being queried is in the Blocked state. */
        eSuspended,       /* The task being queried is in the Suspended state, 
                             or is in the Blocked state with an infinite time out. */
        eDeleted,         /* The task being queried has been deleted, but its TCB has not yet been freed. */
        eInvalid          /* Used as an 'invalid state' value. */
    } eTaskState;
    

    5.7 设置任务的tag(标签)值

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxTagValue );
    

    函数描述:设置任务的标签值,标签值的具体函数和用法由用户决定。FreeRTOS内核不会使用这个标签值。如果要使用这个函数必须将configUSE_APPLICATION_TASK_TAG宏置为1。

    函数参数:xTask:任务句柄,如果设为NULL表示设置自身任务的标签值。

    pxTagValue:要设置的标签值,这是一个TaskHookFunction_t类型的函数指针,也可以设置为其它值。

    返回值:

    5.8 获取任务的tag(标签)值

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask );
    

    函数描述:获取任务的tag(标签)值,任务控制块中有个成员变量pxTaskTag来保存任务的标签值。标签的功能由用户决定。内核一般不会使用这个标签值。使用这个函数需要将configUSE_APPLICATION_TASK_TAG 宏置为1。

    函数参数:xTask:任务句柄

    返回值:任务的标签值。

    5.9 获取当前任务句柄

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    TaskHandle_t xTaskGetCurrentTaskHandle( void );
    

    函数描述:获取当前任务(运行态)的任务的句柄。其实获取到的就是任务控制块。使用这个函数需要将INCLUDE_xTaskGetCurrentTaskHandle宏置为1。

    函数参数:

    返回值:当前任务的任务句柄。

    5.10 获取某个任务句柄

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );
    

    函数描述:根据任务名回去任务句柄。使用xTaskCreate()或者xTaskCreateStatic()函数创建任务时,有一个pcName参数,这个参数就是存放的任务名。xTaskGetHandle就是通过这个任务名来查询任务句柄的。使用这个函数必须将INCLUDE_xTaskGetHandle宏置为1。

    函数参数:任务名,C语言字符串。

    返回值:没有找到pcNameToQuery对应的任务返回NULL;找到了返回对应的任务句柄。

    5.11 获取空闲任务的句柄

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    TaskHandle_t xTaskGetIdleTaskHandle( void );
    

    函数描述:获取空闲任务的任务句柄。使用这个函数需要将INCLUDE_xTaskGetIdleTaskHandle宏置为1。

    函数参数:

    返回值:空闲函数的任务句柄。

    5.12 检查任务堆栈剩余大小

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
    

    函数描述:每个任务都有自己的堆栈。当任务创建的时候指定了堆栈的大小。这个函数用于检查任务从创建到现在的历史剩余最小值。值越小说明堆栈溢出的可能性越大。FreeRTOS把这个历史最小值叫做”高水位线“。使用此函数需要将INCLUDE_uxTaskGetStackHighWaterMark宏设置为1。

    函数参数:xTask:要查询的任务句柄。参数为NULL表示查询任务自身。

    返回值:任务堆栈的”高水位线“值,也就是历史剩余最小值。

    5.13 获取任务名

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    char * pcTaskGetName( TaskHandle_t xTaskToQuery );
    

    函数描述:获取任务的任务名。

    函数参数:xTask:要查询的任务句柄,此参数为NULL表示查询自身任务。

    返回值:返回任务所对应的任务名。

    5.14 查询任务调度器计数器值

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    TickType_t xTaskGetTickCount( void );
    

    函数描述:查询任务调度器从启动到现在时间计数器xTickCount的值。

    函数参数:

    返回值:时间计数器xTickCount的值

    xTickCount:是系统的时钟节拍值,并不是真实的时间值。每个滴答定时器中断xTickCount就会加1,1秒滴答定时器中断多少次取决于宏configTICK_RATE_HZ。理论上xTickCount存在溢出的问题,但是这个溢出对内核没有影响,如果用户有使用的话就要考虑溢出。什么时候溢出取决于宏configUSE_16_BIT_TICKS,此宏为1的时候xTickCount为16位的变量,此宏为0的时候xTickCount为32位的变量。

    5.15 获取系统任务数

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    UBaseType_t uxTaskGetNumberOfTasks( void );
    

    函数描述:获取系统当前任务数

    函数参数:

    返回值:当前系统中的任务数量。包括挂起态、阻塞态、就绪态、空闲任务、运行态任务。

    5.16 获取任务列表

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    void vTaskList( char *pcWriteBuffer );
    

    函数描述:获取任务的详细信息。函数会创建一个表格来描述每个任务的详细信息。使用这个函数必须将configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS宏置为1。

    函数参数:pcWriteBuffer保存任务状态信息的存储,这个存储要足够大。

    返回值:无。

    任务的详细信息如下:

    image-20210801205218591

    Name:任务名

    State:任务状态。X:任务正在执行;B:阻塞态;R:就绪态;S:挂起态;D:任务已经被删除。

    Priority:任务优先级

    Stack:任务堆栈”高水位线“,也就是堆栈历史最小剩余大小。

    Num:任务编号,这个编号是唯一的。当多个任务使用同一个任务名时,可以使用这个编号进行区分。

    5.17 统计任务的运行时间信息

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    void vTaskGetRunTimeStats( char *pcWriteBuffer );
    

    函数描述:获取任务的运行时间统计信息。使用这个函数必须将configGENERATE_RUN_TIME_STATS和configUSE_STATS_FORMATTING_FUNCTIONS宏置为1。

    如果宏configGENERATE_RUN_TIME_STATS设置为1,还需要定义下列的宏:

    portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():此宏用来初始化一个外设来提供时间统计功能所需要的时基,一般是定时器/计数器。这个时基的分辨率一定要比FreeRTOS的系统时钟高,一般设置为比系统时钟高10~20倍。

    portGET_RUN_TIME_COUNTER_VALUE()或者portALT_GET_RUN_TIME_COUNTER_VALUE(Time):这两个宏实现其中一个即可。用于获取当前的时基的时间值。

    函数参数:pcWriteBuffer保存任务运行时间信息的存储,这个存储要足够大。

    返回值:

    任务的运行时间统计信息如下:

    image-20210801210902871

    任务的统计信息提供了每个任务获取到的CPU使用权总的时间。表里面提供了每个任务的运行时间和其所占总时间的百分比。

    5.18 设置线程本地存储指针的值

    函数原型:

    #include “FreeRTOS.h
    #include “task.h”
    void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,
                                           BaseType_t xIndex, void *pvValue );
    

    函数描述:此函数用于设置线程本地存储指针的值,每个任务都有自己的指针数组来作为线程本地存储,使用这些线程本地存储可以用来在任务控制块中存储一些应用信息,这些信息只属于任务自己。线程本地存储指针数组的大小由configNUM_THREAD_LOCAL_STORAGE_POINTERS宏决定。如果要使用这个函数,这个宏就能设置为0。

    函数参数:xTaskToSet:任务句柄,如果设为NULL表示自身任务。xIndex:要设置的线程本地存储指针数组的索引。pvValue:要存储的值。

    返回值:

    5.19 获取线程本地存储指针的值

    函数原型:

    #include “FreeRTOS.h”
    #include “task.h”
    void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery,
                                             BaseType_t xIndex );
    

    函数描述:此函数用于获取线程本地存储指针的值,如果要使用这个函数configNUM_THREAD_LOCAL_STORAGE_POINTERS宏不能设置为0。

    函数参数:xTaskToQuery:任务句柄,如果设为NULL表示自身任务。xIndex:要设置的线程本地存储指针数组的索引。

    返回值:获取到的线程本地存储指针的值。

    5.19 任务状态查询API函数实验

    目的:学习使用任务状态查询相关API函数,包括uxTaskGetSystemState()、vTaskGetInfo()、eTaskGetState()、vTaskList()。

    设计:创建query_task,用于任务状态和信息查询任务,此任务中使用任务状态和信息相关的API函数。创建print_task:间隔1s不停打印计数信息,提示系统正在运行。

    测试代码:

    configSTACK_DEPTH_TYPE Print_Task_STACK_SIZE = 5;
    UBaseType_t  Print_Task_Priority = 1;
    TaskHandle_t Print_xhandle;
    
    configSTACK_DEPTH_TYPE Query_Task_STACK_SIZE = 20;
    UBaseType_t  Query_Task_Priority = 2;
    TaskHandle_t Query_xhandle;
    
    char InfoBuffer[1000];
    void print_task_code(void *para)
    {
        static unsigned int cnt = 0;
    
        for (;;)
        {
            PRINT(" print task cnt %u...", cnt);
            cnt++;
            vTaskDelay(1000);
        }
    }
    
    void query_task_code(void *para)
    {
        unsigned int totalRunTime;
        UBaseType_t  arraySize, x;
        TaskStatus_t *statusArray;
    
        PRINT("----------- uxTaskGetSystemState() ---------------");
        arraySize = uxTaskGetNumberOfTasks();
        statusArray = pvPortMalloc(arraySize * sizeof(TaskStatus_t));
        if (statusArray != NULL)
        {
            arraySize = uxTaskGetSystemState(statusArray, arraySize, &totalRunTime);
            PRINT("TaskName      TaskPriority    TaskNumber");
            for (x = 0; x < arraySize; x++)
            {
                PRINT("%-16s%-16d%-2d",
                    statusArray[x].pcTaskName,
                    statusArray[x].uxCurrentPriority,
                    statusArray[x].xTaskNumber);
            }
        }
        vPortFree(statusArray);
        PRINT("----------- uxTaskGetSystemState() end -----------
    ");
        
        PRINT("----------- vTaskGetInfo() ----------------");
        TaskHandle_t taskHandle;
        TaskStatus_t taskStatus;
        taskHandle = xTaskGetHandle("print task");
        vTaskGetInfo(taskHandle, &taskStatus, pdTRUE, eInvalid);
        PRINT("              task name: %s", taskStatus.pcTaskName);
        PRINT("            task number: %d", taskStatus.xTaskNumber);
        PRINT("             task state: %d", taskStatus.eCurrentState);
        PRINT("  task current priority: %d", taskStatus.uxCurrentPriority);
        PRINT("     task base priority: %d", taskStatus.uxBasePriority);
        PRINT("task stack base address: 0x%x", taskStatus.pxStackBase);
        PRINT("   task high water mark: %d", taskStatus.usStackHighWaterMark);
        PRINT("  task run time counter: %d",taskStatus.ulRunTimeCounter);
        PRINT("----------- vTaskGetInfo() end ------------
    ");
    
        PRINT("----------- eTaskGetState() ----------------");
        eTaskState taskState;
        char *state_str[] = {"running", "ready", "blocked", "suspended", "deleted", "invalid"};
        taskHandle = xTaskGetHandle("query task");
        taskState = eTaskGetState(taskHandle);
        PRINT("task state:%s", state_str[taskState]);
        PRINT("----------- eTaskGetState() end ------------
    ");
    
        PRINT("----------- vTaskList() ----------------");
        PRINT("Name         State  Priority   Stack   Num");
        PRINT("******************************************");
        vTaskList(InfoBuffer);
        PRINT("%s", InfoBuffer);
        PRINT("----------- vTaskList() end ------------
    ");
        
        for (;;)
        {
            static unsigned int cnt = 0;
            PRINT(" query task cnt %u...", cnt);
            cnt++;
            vTaskDelay(1000);
        }
    }
    
    void creat_task(void)
    {
        if (xTaskCreate(print_task_code, "print task", 
            Print_Task_STACK_SIZE, NULL, Print_Task_Priority,
            &Print_xhandle) != pdPASS)
        {
            PRINT("creat task failed!
    ");
        }
        
        if (xTaskCreate(query_task_code, "query task", 
            Query_Task_STACK_SIZE, NULL, Query_Task_Priority,
            &Query_xhandle) != pdPASS)
        {
            PRINT("creat task failed!
    ");
        }
        vTaskStartScheduler();
    }
    

    编译、运行,结果如下:

    $ ./build/freertos-simulator 
    ----------- uxTaskGetSystemState() ---------------
    TaskName      TaskPriority    TaskNumber
    query task      2               2 
    print task      1               1 
    IDLE            0               3 
    Tmr Svc         30              4 
    ----------- uxTaskGetSystemState() end -----------
    
    ----------- vTaskGetInfo() ----------------
                  task name: print task
                task number: 1
                 task state: 1
      task current priority: 1
         task base priority: 1
    task stack base address: 0x868010
       task high water mark: 0
      task run time counter: 0
    ----------- vTaskGetInfo() end ------------
    
    ----------- eTaskGetState() ----------------
    task state:running
    ----------- eTaskGetState() end ------------
    
    ----------- vTaskList() ----------------
    Name         State  Priority   Stack   Num
    ******************************************
    query task      X       2       15      2
    print task      R       1       0       1
    IDLE            R       0       65      3
    Tmr Svc         B       30      135     4
    
    ----------- vTaskList() end ------------
    
     query task cnt 0...
     print task cnt 0...
     query task cnt 1...
     print task cnt 1...
    

    可以得到,“print task”任务处于就绪态,任务优先级为1,栈空间已经用完了。任务编号为1。“query task”处于运行态,任务优先级为2,栈空间剩余15,任务编号为2。

  • 相关阅读:
    [Java多线程]-并发,并行,synchonrized同步的用法
    [大数据可视化]-saiku的源码打包运行/二次开发构建
    [大数据可视化]-saiku的源码包Bulid常见问题和jar包
    [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)
    [机器学习]-PCA数据降维:从代码到原理的深入解析
    [Java多线程]-Thread和Runable源码解析之基本方法的运用实例
    [Java多线程]-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
    [Java多线程]-Thread和Runable源码解析
    [机器学习]-Adaboost提升算法从原理到实践
    月是故乡明
  • 原文地址:https://www.cnblogs.com/mrlayfolk/p/15088125.html
Copyright © 2011-2022 走看看