从零开始学ESP32:个人笔记记录:
芯片型号: ESP32
网络环境支持:LWIP
IDF.PY-SDK: ESP-IDF v4.3
芯片功能: freeRTOS系统
声明: 进行事件异步操作,或者非阻塞操作时候,单体循环需要处理额外的短事件时,重新考量了决定要写一个类线程池的功能,仅仅作为一个短事件处理。
// 线程池池部结构体
#define THREAD_NUMBER_MAX 128
typedef struct ST_THREAD_POOL_
{
int threadNumber; // 当前线程个数
void* threadReadLock; // 当前各个线程读锁
void* threadWriteLock; // 当前操作线程写锁
void* threadPollQueue; // 线程处理队列
TaskHandle_t threadHandleArray[ THREAD_NUMBER_MAX ]; //不用静态数组、可以使用链表
}st_threadPool,*st_threadPool_t;
// 线程池队列式处理
typedef struct ST_THREAD_QUEUE__
{
int (*function)( void* );
void* funcArgv;
}st_threadQueum,*st_threadQueum_t;
/*****************************************************************************
- @file
- @class
- @brief
- @param
- @warning
- @return
- @author 线程池中主处理
- @date 2021-11-17 14:48
*/
static void T_threadPoolFunc( st_threadPool_t tp )
{
int ret = 0;
int (*fun)(void*) = NULL;
st_threadQueum queue ={ 0 };
while( 1 )
{
xSemaphoreTake( tp->threadReadLock , portMAX_DELAY ); // 上锁
do{
memset( &queue , 0, sizeof( st_threadQueum ) );
ret = xQueueReceive( tp->threadPollQueue , &queue , portMAX_DELAY ); //进行队列等待,读取队列数据
}while( ret == pdFALSE );
xSemaphoreGive(tp->threadReadLock) ; // 解锁
fun = queue.function;
if( fun )
{
ret = (*fun)( queue.funcArgv ); // 函数指针回调
if( ret == -1 ) { break; } // 返回-1 就会将当前线程释放
}
}
vTaskDelete( NULL );
return;
}
/*****************************************************************************
- @file
- @class st_threadPool_t
- @class st_threadQueum_t
- @brief 创建线程池任务
- @param NULL
- @warning NULL
- @return 失败返回 NULL 成功返回 thread-dev
- @author
- @exception 当线程池线程超过一定数量,会导致内存耗尽而奔溃,配合内存操作
*/
void* T_threadPoolCreate( int threadNum ,char* name ,int stackSize, int priority )
{
int i = 0;
int ret = 0;
char threadName[36] = { 0 };
st_threadPool_t tp = (st_threadPool_t)malloc( sizeof( st_threadPool ) );
if( !tp ) { goto Err_malloc; }
memset( tp , 0 , sizeof( st_threadPool ));
/*
由于FreeRTOS的新旧版本的API不同,导致现象不同于预期,问题就在xSemaphoreCreateBinary与vSemaphoreCreateBinary的区别
用vSemaphoreCreateBinary创建的二元信号量,初始值为“满”,因为创建的同时释放了信号量
需要调用xSemaphoreGive初始化当前信号量
*/
tp->threadReadLock = xSemaphoreCreateBinary();
if( !tp->threadReadLock ) { goto Err_rMutex; }
xSemaphoreGive( tp->threadReadLock );
tp->threadWriteLock = xSemaphoreCreateBinary();
if( !tp->threadWriteLock ) { goto Err_wMutex; }
xSemaphoreGive( tp->threadWriteLock );
tp->threadPollQueue = xQueueCreate( threadNum + 1, sizeof( st_threadQueum ) );
if( !tp->threadPollQueue ) { goto Err_queue; }
for( i = 0; i < threadNum ; i++ )
{
snprintf(threadName , 32 ,"tp%s_%d", name , i );
ret = xTaskCreate( T_threadPoolFunc , threadName ,stackSize ,tp, priority, &tp->threadHandleArray[ i ] );
if( ret != pdPASS ) { goto Err_thFork; }
}
tp->threadNumber = i;
return tp;
Err_thFork : for(int j = 0; j < i ; j++ ) { vTaskDelete( &tp->threadHandleArray[ i ] ); }
vQueueDelete ( tp->threadPollQueue );
Err_queue : vSemaphoreDelete( tp->threadWriteLock );
Err_wMutex : vSemaphoreDelete( tp->threadReadLock );
Err_rMutex : free( tp );
Err_malloc : return NULL;
}
/*****************************************************************************
- @file NAXC_TOOLS.c
- @class st_threadPool_t
- @class st_threadQueum_t
- @brief 释放整个线程池,正在运行的任务,有系统任务管理器暂停。并不是实时释放
- @brief 等待空闲线程进行任务线程回收
- @param NULL
- @warning NULL
- @return void
- @author HML
- @exception
*/
void T_threadPoolDelete( st_threadPool_t tp )
{
if( !tp ){ return ; }
xSemaphoreTake( tp->threadWriteLock , portMAX_DELAY ); //上锁
tp->threadPollQueue = NULL;
for(int i = 0; i < tp->threadNumber ; i++ )
{
vTaskDelete( tp->threadHandleArray[ i ] );
tp->threadHandleArray[ i ] = NULL ;
}
vQueueDelete ( tp->threadPollQueue );
xSemaphoreGive ( tp->threadWriteLock );
vSemaphoreDelete( tp->threadWriteLock ); tp->threadWriteLock = NULL;
vSemaphoreDelete( tp->threadReadLock ); tp->threadReadLock = NULL;
free( tp );
tp = NULL;
}
/*****************************************************************************
- @file NAXC_TOOLS.c
- @class st_threadPool_t
- @class st_threadQueum_t
- @brief 向线程池中,发送任务。
- @brief
- @param NULL
- @warning NULL
- @return void
- @author HML
- @exception
*/
int T_threadPoolAddTask( st_threadPool_t tp, int (*func)(void* argv ), void* argv , int timeOut_ticks )
{
int temp = 0;
st_threadQueum queueObj = { 0 };
if( !tp || !tp->threadWriteLock || !tp->threadPollQueue ) { return -1; }
xSemaphoreTake( tp->threadWriteLock , portMAX_DELAY ); //上锁
queueObj.function = func;
queueObj.funcArgv = argv;
temp = xQueueSend( tp->threadPollQueue, &queueObj , timeOut_ticks);
xSemaphoreGive( tp->threadWriteLock ) ; //解锁
return temp;
}
/**************************** 测试函数 *************************************************
- @file NAXC_TOOLS.c
- @class st_threadPool_t
- @class st_threadQueum_t
- @brief 测试函数
- @brief
- @param NULL
- @warning NULL
- @return void
- @author HML
- @exception
*/
int test_1(void* argc )
{
for ( int i = 0 ; i < 100; i++ )
{
printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
}
return 0;
}
int test_2(void* argc )
{
for ( int i = 0 ; i < 100; i++ )
{
printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
}
return 0;
}
int test_0(void* argc )
{
for ( int i = 0 ; i < 100; i++ )
{
printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
}
return 0;
}
int test_threadPool_main( void * argc )
{
printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
os_time_dly( 100 * 5 );
printf("__[%s - %d] \r\n", __FUNCTION__, __LINE__ );
void* tp_fd = T_threadPoolCreate( 4 , "thTest" , 1 * 1024 , 10 );
if( tp_fd != NULL )
{
T_threadPoolAddTask( tp_fd , test_0 , NULL , 10 );
T_threadPoolAddTask( tp_fd , test_1 , NULL , 10 );
T_threadPoolAddTask( tp_fd , test_2 , NULL , 10 );
}
sleep ( 1000 );
T_threadPoolDelete( tp_fd );
return 0;
}
在当前这个版本线程池里面存在尚未解决的问题或者是其他问题,在使用时候必须要考虑到:
一、为什么在将函数加入到线程池执行前还要额外上写锁,在频繁的测试,和多线程同时调用线程池时候发现,FREERTOS的队列对线程竟态下有一定几率出现一个死机状态,提示着这一行代码(QueueGenericSend 709 )然后系统进入卡顿,直到被看门狗拉重启,所以我在发队列之前加多一把锁的原因。
二、将短处理函数放进线程池处理的时候,返回值就是队列返回值,这样处理的原因是,用户必须感知自己调用线程池时候,当前池部是否为满的。
三、里面存在一个理论逻辑BUG,就是用户无法感知当前内存池是否已经满,但是队列的空间还是空的一个反馈机制(就是全部线程都在处理完了,但是处理一个函数就有一条线程启动,但是启动之前式将队列读出来的,所以就会有这样一个问题,函数正在被处理,但是队列却空闲出来了)。 这一块在后续继续完善。
要是碰巧看到这边博文的工程师,并且有想法或者是对freeRTOS比较熟系的,希望您路过的时候不设赐教。
当前用上你的想法或者对应的方案,博文也会明确表示当您的身份和对应的贡献。
感谢。
抄袭割你JJ