从零开始学ESP32:(五)ESP32/freeRTOS 实现一个线程池(池化)操作

从零开始学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

在MATLAB中实现多个视频在同一窗口内同播放,可以使用`VideoPlayer`函数配合一些循环和同步操作。以下是简单的步骤: 1. **导入视频**:首先,你需要读取所有你想播放的视频文件。假设你有多个视频文件如`video1.mp4`, `video2.mp4`, 等等,你可以用`VideoReader`函数来打开它们。 ```matlab videoFiles = {'video1.mp4', 'video2.mp4', 'video3.mp4'}; % 改为你实际的视频路径 videoPlayers = cell(size(videoFiles)); for i = 1:numel(videoFiles) videoPlayers{i} = VideoReader(videoFiles{i}); end ``` 2. **创建视频播放器**:然后为每个视频创建一个`VideoPlayer`对象,并设置其显示位置。 ```matlab figure('Name', 'Multi-Video Player'); % 创建一个新的窗口 positions = [(0.05, 0.2), (0.3, 0.2), (0.55, 0.2)]; % 可调整显示位置 for i = 1:numel(videoPlayers) videoPlayers{i}.WindowStyle = 'slider'; % 设置滑动条控制 videoPlayers{i}.Position = positions(i,:); open(videoPlayers{i}); % 打开视频 end ``` 3. **循环播放**:使用`while`循环和`readFrame`函数来逐帧读取并显示每个视频,直到视频结束。 ```matlab currentTime = 0; playState = true; % 初始状态为播放 while playState for i = 1:numel(videoPlayers) frame = readFrame(videoPlayers{i}); if isempty(frame) || ~isprop(videoPlayers{i}, 'CurrentTime') % 视频结束 close(videoPlayers{i}); videoPlayers{i} = VideoReader(videoFiles{i}); currentTime = 0; else currentTime = videoPlayers{i}.CurrentTime; % 更新当前间 set(videoPlayers{i}, 'CurrentTime', currentTime); % 跳到指定间 drawnow; % 暂停一段间后再继续播放下一个 end % 添加同步逻辑,如果需要所有视频在同一间点暂停或播放 % ... end pause(0.01); % 缩短暂停间以加快播放速度,可根据需要调整 playState = any([videoPlayers{:}.IsPlaying]); % 如果有任何一个视频还在播放,设为true end close(gcf); % 关闭窗口 ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值