ThreadX_note:创建线程

ThreadX 创建线程

ThreadX 是一款实时操作系统 (RTOS),它提供了一套全面的 API,可以用于创建和管理线程。
在这里插入图片描述

创建线程

在 ThreadX 中,我们可以使用 tx_thread_create 函数来创建线程。

exam:

#include "tx_api.h"

/* 定义任务堆栈大小 */
#define TASK_STACK_SIZE 1024

/* 定义任务优先级 */
#define TASK_PRI 16

/* 定义两个任务的ID */
TX_THREAD thread_0;
TX_THREAD thread_1;

/* 定义任务堆栈 */
uint8_t thread_0_stack[TASK_STACK_SIZE];
uint8_t thread_1_stack[TASK_STACK_SIZE];

/* 任务入口函数 */
void thread_0_entry(ULONG thread_input) {
    /* 执行任务0的操作 */
}

void thread_1_entry(ULONG thread_input) {
    /* 执行任务1的操作 */
}

/* 程序入口函数 */
int main(void) {
    /* 初始化STM32硬件 */
    /* 初始化ThreadX内核 */
    tx_kernel_enter();

    /* 创建任务0 */
    tx_thread_create(&thread_0, "thread_0", thread_0_entry, 0, 
                     thread_0_stack, TASK_STACK_SIZE, 
                     TASK_PRI, TASK_PRI, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* 创建任务1 */
    tx_thread_create(&thread_1, "thread_1", thread_1_entry, 0, 
                     thread_1_stack, TASK_STACK_SIZE, 
                     TASK_PRI, TASK_PRI, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* 启动ThreadX内核 */
    tx_kernel_start();

    /* 永远不会运行到这里 */
    return 0;
}

tx_thread_create 函数解析

tx_thread_create 函数是 ThreadX 库中用于创建线程的函数。它的原型如下:

UINT tx_thread_create(TX_THREAD *thread_ptr, CHAR *name_ptr, 
                      VOID (*entry_function)(ULONG), ULONG entry_input, 
                      VOID *stack_start, ULONG stack_size, 
                      UINT priority, UINT preempt_threshold, 
                      ULONG time_slice, UINT auto_start);

下面对 tx_thread_create 函数的各个参数进行解析:

  • thread_ptr:指向 TX_THREAD 类型的指针,用于保存新创建线程的控制块信息。
  • name_ptr:线程的名称,以字符串形式提供。
  • entry_function:线程的入口函数,即线程创建后要执行的函数。它必须是一个无返回值(void),接受 ULONG 类型参数的函数指针,因为 ThreadX 会将传递给线程的参数作为 entry_input 传递给入口函数。
  • entry_input:线程入口函数的输入参数。可以是任何类型的数据,但将其转换为 ULONG 类型并在入口函数中进行相应的类型转换。
  • stack_start:线程堆栈的起始地址。通常通过定义一个数组,并将数组的地址作为参数传递给此处,用于分配线程的堆栈空间。
  • stack_size:线程堆栈的大小,以字节为单位。
  • priority:线程的优先级。ThreadX 使用固定优先级调度算法来确定线程执行的顺序,优先级较高的线程将在优先级较低的线程之前执行。
  • preempt_threshold:线程的抢占阈值。当一个线程的优先级高于或等于抢占阈值时,它可以抢占当前正在执行的线程。
  • time_slice:线程的时间片大小。时间片是指线程在多任务系统中执行的时间段。如果设置为 0,则禁用时间片调度。
  • auto_start:指示线程是否在创建后自动启动的标志。如果设置为 TX_AUTO_START,则线程在创建后立即运行。如果设置为其他非零值或 0,则线程需要手动启动。

tx_thread_create 函数返回一个无符号整数类型的值,表示函数执行的状态。常见的返回值包括 TX_SUCCESS(成功)和 TX_PTR_ERROR(参数错误)等。

通过使用 tx_thread_create 函数,可以在 ThreadX 环境中创建新的线程,并为其指定入口函数、堆栈、优先级等参数。这样就可以在 STM32 微控制器上同时运行多个线程,实现多任务处理和并发执行的应用程序。

在 ThreadX 中,线程入口函数只接受一个 ULONG 类型的参数。如果你需要传递多个参数给线程,你可以使用一个结构体来封装这些参数,然后将结构体的地址转换为 ULONG 类型并传递给线程。

exam:

#include "tx_api.h"

/* 定义任务堆栈大小 */
#define TASK_STACK_SIZE 1024

/* 定义任务优先级 */
#define TASK_PRI 16

/* 定义任务的ID */
TX_THREAD thread;

/* 定义任务堆栈 */
uint8_t thread_stack[TASK_STACK_SIZE];

/* 定义任务参数结构体 */
typedef struct {
    int param1;
    float param2;
    char* param3;
} thread_params_t;

/* 定义任务参数 */
thread_params_t thread_params = {123, 456.78, "Hello, ThreadX"};

/* 任务入口函数 */
void thread_entry(ULONG thread_input) {
    /* 将 thread_input 转换回原来的结构体指针 */
    thread_params_t* params = (thread_params_t*)thread_input;

    /* 现在你可以使用 params->param1, params->param2 和 params->param3 了 */
}

/* 程序入口函数 */
int main(void) {
    /* 初始化STM32硬件 */
    /* 初始化ThreadX内核 */
    tx_kernel_enter();

    /* 创建任务,将结构体的地址转换为 ULONG 类型并传递给线程 */
    tx_thread_create(&thread, "thread", thread_entry, (ULONG)&thread_params, 
                     thread_stack, TASK_STACK_SIZE, 
                     TASK_PRI, TASK_PRI, TX_NO_TIME_SLICE, TX_AUTO_START);

    /* 启动ThreadX内核 */
    tx_kernel_start();

    /* 永远不会运行到这里 */
    return 0;
}

以上代码演示了如何创建一个带有多个参数的任务并启动 ThreadX 内核。每个任务都有一个独立的堆栈和一个入口函数,可以在其中执行任务的操作。任务的参数通过 tx_thread_create 函数的 entry_input 参数传递给任务的入口函数。

void *venc_stream_thread_func(void *data) { int ret; int i; struct test_resource *res = (struct test_resource *)data; VENC_STREAM_S stream_video; // 定义帧码流类型结构体。 VENC_CHN_STATUS_S status; // 定义编码通道的状态结构体。 XMEDIA_S32 maxfd = 0; struct timeval timeout_val; fd_set read_fds; XMEDIA_S32 chn_num; VENC_CHN venc_chn; XMEDIA_S32 venc_fd[VENC_MAX_CHN_NUM]; if (res->venc_snap == XMEDIA_TRUE) { chn_num = res->venc_chn_num - 1; } else { chn_num = res->venc_chn_num; } for (i = 0; i < chn_num; i++) { venc_fd[i] = XMEDIA_API_VENC_GetFd(res->venc_chn[i]); // 获取编码通道对应的设备文件句柄。 if (venc_fd[i] < 0) { DEMO_PRINT("XMEDIA_API_VENC_GetFd failed with %#x!\n", venc_fd[i]); return NULL; } if (maxfd <= venc_fd[i]) { maxfd = venc_fd[i]; } } while (res->venc_stream_thread_run) { FD_ZERO(&read_fds); for (i = 0; i < chn_num; i++) { FD_SET(venc_fd[i], &read_fds); } timeout_val.tv_sec = 0; timeout_val.tv_usec = 100000; ret = select(maxfd + 1, &read_fds, NULL, NULL, &timeout_val); if (ret < 0) { DEMO_PRINT("select failed!\n"); break; } else if (ret == 0) { DEMO_PRINT("get venc stream time out, exit thread\n"); continue; } else { for (i = 0; i < chn_num; i++) { if (FD_ISSET(venc_fd[i], &read_fds)) { /******************************************************* step 2.1 : query how many packs in one-frame stream. *******************************************************/ memset(&stream_video, 0, sizeof(stream_video)); venc_chn = res->venc_chn[i]; ret = XMEDIA_API_VENC_QueryStatus(venc_chn, &status); // 查询编码通道状态。 if (XMEDIA_SUCCESS != ret) { DEMO_PRINT("XMEDIA_API_VENC_QueryStatus chn[%d] res->venc_chn[%d]=%dfailed with %#x!\n", venc_chn, i, res->venc_chn[i], ret); // break; return NULL; } /******************************************************* step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example: if(0 == status.u32CurPacks || 0 == status.u32LeftStreamFrames) { DEMO_PRINT("NOTE: Current frame is NULL!\n"); continue; } *******************************************************/ if (0 == status.u32CurPacks) { DEMO_PRINT("NOTE: Current frame is NULL!\n"); continue; } /******************************************************* step 2.3 : malloc corresponding number of pack nodes. *******************************************************/ stream_video.pstPack = (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S) * status.u32CurPacks); if (NULL == stream_video.pstPack) { DEMO_PRINT("malloc stream pack failed!\n"); break; } /******************************************************* step 2.4 : call mpi to get one-frame stream *******************************************************/ stream_video.u32PackCount = status.u32CurPacks; ret = XMEDIA_API_VENC_GetStream(venc_chn, &stream_video, XMEDIA_TRUE); // 获取编码码流。 if (XMEDIA_SUCCESS != ret) { free(stream_video.pstPack); stream_video.pstPack = NULL; DEMO_PRINT("XMEDIA_API_VENC_GetStream failed with %#x!\n", ret); break; } // modify by dgb if (res->save_mode == 1) { for (int w = 0; w < stream_video.u32PackCount; w++) { fwrite(stream_video.pstPack[w].pu8Addr + stream_video.pstPack[w].u32Offset, stream_video.pstPack[w].u32Len - stream_video.pstPack[w].u32Offset, 1, res->esfile[i]); fflush(res->esfile[i]); } } else if (res->save_mode == 0) { // zn是rtsp流的形式输出 if (i == 0 && g_xy_info.isConnect == 1) { // 通过队列向sdk线程发送视频数据 DispthVtoXy(res->venc_payload[i], &stream_video); // modify by dgb for } // app_liveserver_send_video_data(i, res->venc_payload[i], &stream_video); } if (XMEDIA_SUCCESS != ret) { free(stream_video.pstPack); stream_video.pstPack = NULL; DEMO_PRINT("save stream failed!\n"); break; } /******************************************************* step 2.6 : release stream *******************************************************/ ret = XMEDIA_API_VENC_ReleaseStream(venc_chn, &stream_video); // 释放码流缓存。 if (XMEDIA_SUCCESS != ret) { DEMO_PRINT("XMEDIA_API_VENC_ReleaseStream failed!\n"); free(stream_video.pstPack); stream_video.pstPack = NULL; break; } /******************************************************* step 2.7 : free pack nodes *******************************************************/ free(stream_video.pstPack); stream_video.pstPack = NULL; } } } } return NULL; } 注释一下代码,不要修改代码
最新发布
07-15
<think>我们被要求注释代码,但不修改代码。因此,我们将逐段添加注释,解释代码的功能和关键点。 注意:代码中已经有一些注释,我们将在原有基础上补充更详细的解释。</think>以下是对视频编码流处理线程的详细注释,严格遵循不修改代码的原则: ```c // 视频编码流处理线程函数 void *venc_stream_thread_func(void *data) { int ret; // 函数返回值 int i; // 循环计数器 // 获取传入的资源结构体指针 struct test_resource *res = (struct test_resource *)data; // 定义视频码流结构体 VENC_STREAM_S stream_video; // 存储视频帧码流数据 // 定义编码通道状态结构体 VENC_CHN_STATUS_S status; // 存储编码通道状态信息 // 文件描述符相关变量 XMEDIA_S32 maxfd = 0; // 最大文件描述符值 struct timeval timeout_val; // select超时时间结构体 fd_set read_fds; // 文件描述符集合 // 编码通道相关变量 XMEDIA_S32 chn_num; // 编码通道数量 VENC_CHN venc_chn; // 当前处理的编码通道 XMEDIA_S32 venc_fd[VENC_MAX_CHN_NUM]; // 编码通道文件描述符数组 // 根据是否启用快照功能确定编码通道数量 if (res->venc_snap == XMEDIA_TRUE) { chn_num = res->venc_chn_num - 1; // 排除快照通道 } else { chn_num = res->venc_chn_num; // 使用所有通道 } // 获取每个编码通道的文件描述符 for (i = 0; i < chn_num; i++) { // 获取编码通道的文件描述符 venc_fd[i] = XMEDIA_API_VENC_GetFd(res->venc_chn[i]); // 检查文件描述符获取是否成功 if (venc_fd[i] < 0) { DEMO_PRINT("XMEDIA_API_VENC_GetFd failed with %#x!\n", venc_fd[i]); return NULL; // 获取失败则退出线程 } // 更新最大文件描述符值 if (maxfd <= venc_fd[i]) { maxfd = venc_fd[i]; } } // 主循环:处理视频编码流 while (res->venc_stream_thread_run) // 线程运行标志控制循环 { // 初始化文件描述符集合 FD_ZERO(&read_fds); // 将编码通道的文件描述符添加到集合中 for (i = 0; i < chn_num; i++) { FD_SET(venc_fd[i], &read_fds); } // 设置select超时时间为100毫秒 timeout_val.tv_sec = 0; timeout_val.tv_usec = 100000; // 使用select等待文件描述符可读 ret = select(maxfd + 1, &read_fds, NULL, NULL, &timeout_val); // 处理select返回值 if (ret < 0) // select错误 { DEMO_PRINT("select failed!\n"); break; // 退出循环 } else if (ret == 0) // 超时 { DEMO_PRINT("get venc stream time out, exit thread\n"); continue; // 继续下一次循环 } else // 有文件描述符可读 { // 遍历所有编码通道 for (i = 0; i < chn_num; i++) { // 检查当前通道是否有数据可读 if (FD_ISSET(venc_fd[i], &read_fds)) { /******************************************************* step 2.1 : 查询当前帧包含多少个数据包 *******************************************************/ // 清空码流结构体 memset(&stream_video, 0, sizeof(stream_video)); // 获取当前编码通道 venc_chn = res->venc_chn[i]; // 查询编码通道状态 ret = XMEDIA_API_VENC_QueryStatus(venc_chn, &status); if (XMEDIA_SUCCESS != ret) { DEMO_PRINT("XMEDIA_API_VENC_QueryStatus chn[%d] res->venc_chn[%d]=%dfailed with %#x!\n", venc_chn, i, res->venc_chn[i], ret); return NULL; // 查询失败则退出线程 } /******************************************************* 建议同时检查u32CurPacks和u32LeftStreamFrames两个状态值 例如: if(0 == status.u32CurPacks || 0 == status.u32LeftStreamFrames) { DEMO_PRINT("NOTE: Current frame is NULL!\n"); continue; } *******************************************************/ // 检查当前帧是否有有效数据 if (0 == status.u32CurPacks) { DEMO_PRINT("NOTE: Current frame is NULL!\n"); continue; // 无有效数据则跳过 } /******************************************************* step 2.3 : 分配对应数量的数据包节点内存 *******************************************************/ // 根据数据包数量分配内存 stream_video.pstPack = (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S) * status.u32CurPacks); if (NULL == stream_video.pstPack) { DEMO_PRINT("malloc stream pack failed!\n"); break; // 内存分配失败则退出循环 } /******************************************************* step 2.4 : 调用API获取一帧视频码流 *******************************************************/ // 设置数据包数量 stream_video.u32PackCount = status.u32CurPacks; // 获取编码码流 ret = XMEDIA_API_VENC_GetStream(venc_chn, &stream_video, XMEDIA_TRUE); if (XMEDIA_SUCCESS != ret) { // 获取失败则释放内存并退出 free(stream_video.pstPack); stream_video.pstPack = NULL; DEMO_PRINT("XMEDIA_API_VENC_GetStream failed with %#x!\n", ret); break; } // 根据保存模式处理数据 if (res->save_mode == 1) // 保存到文件模式 { // 遍历所有数据包 for (int w = 0; w < stream_video.u32PackCount; w++) { // 将数据写入文件 fwrite(stream_video.pstPack[w].pu8Addr + stream_video.pstPack[w].u32Offset, stream_video.pstPack[w].u32Len - stream_video.pstPack[w].u32Offset, 1, res->esfile[i]); // 刷新文件缓冲区 fflush(res->esfile[i]); } } else if (res->save_mode == 0) // RTSP流输出模式 { // 主通道且已连接时发送到XY平台 if (i == 0 && g_xy_info.isConnect == 1) { // 通过队列向SDK线程发送视频数据 DispthVtoXy(res->venc_payload[i], &stream_video); } // 注释掉的直播服务器发送功能 // app_liveserver_send_video_data(i, res->venc_payload[i], &stream_video); } // 检查处理结果 if (XMEDIA_SUCCESS != ret) { free(stream_video.pstPack); stream_video.pstPack = NULL; DEMO_PRINT("save stream failed!\n"); break; } /******************************************************* step 2.6 : 释放码流资源 *******************************************************/ // 释放码流缓存 ret = XMEDIA_API_VENC_ReleaseStream(venc_chn, &stream_video); if (XMEDIA_SUCCESS != ret) { DEMO_PRINT("XMEDIA_API_VENC_ReleaseStream failed!\n"); free(stream_video.pstPack); stream_video.pstPack = NULL; break; } /******************************************************* step 2.7 : 释放数据包节点内存 *******************************************************/ // 释放数据包内存 free(stream_video.pstPack); stream_video.pstPack = NULL; } } } } return NULL; // 线程退出 } ``` ### 关键点解释: 1. **线程初始化**: - 获取资源结构体指针 - 根据配置确定处理的编码通道数量 - 获取每个编码通道的文件描述符 - 计算最大文件描述符用于select 2. **主循环设计**: - 使用`res->venc_stream_thread_run`标志控制线程运行 - 使用`FD_ZERO`和`FD_SET`初始化文件描述符集合 - 设置100ms超时防止永久阻塞 - 使用`select`等待编码数据就绪 3. **数据处理流程**: - **状态查询**:使用`XMEDIA_API_VENC_QueryStatus`检查通道状态 - **内存分配**:根据状态中的包数分配内存 - **获取码流**:使用`XMEDIA_API_VENC_GetStream`获取编码数据 - **数据处理**: - 文件模式:将数据写入文件并刷新缓冲区 - 流模式:通过队列发送到SDK线程 - **资源释放**: - 使用`XMEDIA_API_VENC_ReleaseStream`释放编码器资源 - 释放分配的数据包内存 4. **错误处理**: - 检查所有API调用的返回值 - 文件描述符获取失败时退出线程 - 内存分配失败时跳出循环 - API调用失败时释放资源并报错 5. **模式处理**: - `save_mode == 1`:文件保存模式 - `save_mode == 0`:RTSP流输出模式 - 主通道(i==0)且已连接时发送到XY平台 ### 设计特点: 1. **多通道支持**: - 使用数组存储多个通道的文件描述符 - 循环处理所有活动通道 - 根据配置动态确定通道数量 2. **高效I/O处理**: - 使用select多路复用技术 - 避免为每个通道创建单独线程 - 100ms超时防止线程空转 3. **资源管理**: - 严格遵循"分配-使用-释放"模式 - 每次循环都释放分配的内存 - 及时释放编码器资源 4. **模块化处理**: - 状态查询、数据获取、处理、释放分离 - 清晰的步骤注释 - 错误处理集中管理 5. **灵活性**: - 支持文件保存和流输出两种模式 - 可扩展的注释建议(同时检查多个状态) - 预留的直播服务器发送接口 6. **线程安全**: - 使用线程运行标志控制循环 - 独立处理每个通道的数据 - 无共享资源冲突
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值