硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1152|回复: 13
收起左侧

[ThreadX全家桶] threadx 互斥信号量无法阻塞线程的问题

[复制链接]

14

主题

54

回帖

96

积分

初级会员

积分
96
发表于 2024-10-7 23:07:25 | 显示全部楼层 |阅读模式
本帖最后由 Superusrss 于 2024-10-7 23:09 编辑

发现一个信号量无法发生阻塞的问题,直接运行打印会发生后来数据覆盖为发送数据的问题,表现为乱码

创建信号量


[C] 纯文本查看 复制代码
TX_MUTEX uart2_mutex;
tx_mutex_create(&uart2_mutex, "uart2_mutex", TX_INHERIT);


使用信号量
[C] 纯文本查看 复制代码
void print_uart2(const char *format, ...)
{
    uint16_t len;

    va_list args;
    va_start(args, format);
// 若使用串口自带标志阻塞就没有问题
    // while (huart2.gState != HAL_UART_STATE_READY)
    //     ;
    // len = vsnprintf((char*)UartTxBuf,sizeof(UartTxBuf)-1,(char*)format,args);
// 若使用信号量,则即使没有mutexput, 也不会阻塞
    ret = tx_mutex_get(&uart2_mutex, TX_WAIT_FOREVER);
    xsnprintf((char *)&UartTxBuf[0], RX_BUFFER_SIZE_UART1, (const char *)format, args);
    len = strlen((char *)UartTxBuf);
    va_end(args);
    HAL_UART_Transmit_IT(&huart2, UartTxBuf, len);
}


中断回调函数中释放信号量
[C] 纯文本查看 复制代码
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        return;
    }
    else if (huart->Instance == USART2)
    {
        if (_tx_thread_system_state == TX_INITIALIZE_IS_FINISHED)
        {
// 我把下面释放信号量注释掉,上面就没有信号量了,理论上应该发生阻塞
            // ret = tx_mutex_put(&uart2_mutex);
        }
    }
}

回复

使用道具 举报

14

主题

54

回帖

96

积分

初级会员

积分
96
 楼主| 发表于 2024-10-8 07:26:25 来自手机 | 显示全部楼层
本帖最后由 Superusrss 于 2024-10-8 11:59 编辑

貌似知道原因了,不知道对不对

mutex 在一个线程中多次get是不会阻塞的,其他线程来get才会阻塞。


下面来自ai的回答

在ThreadX中,互斥锁(Mutex)通常是递归的,这意味着同一个线程可以多次获取同一个互斥锁,而不会阻塞自己。这种设计允许线程在不同的代码段重复获取和释放互斥锁,而不会因为已经持有互斥锁而阻塞自己。互斥锁内部有一个计数器来跟踪同一个线程获取互斥锁的次数,只有当释放次数与获取次数相匹配时,互斥锁才会被真正释放,其他线程才有机会获取它。

然而,如果你需要实现一个互斥锁,使得同一个线程再次尝试获取已经持有的互斥锁时会被阻塞,那么你可以考虑不使用ThreadX提供的递归互斥锁,而是自己实现一个非递归的互斥锁。这通常需要额外的逻辑来检查持有互斥锁的线程是否尝试再次获取它,并在这种情况下使线程挂起。

ThreadX的互斥锁API包括创建、删除、获取和释放互斥锁的功能。例如,`tx_mutex_get` 函数用于获取互斥锁,如果互斥锁已经被其他线程持有,那么调用线程将会被阻塞,直到互斥锁可用。如果你想让线程在尝试获取已经持有的互斥锁时阻塞自己,你需要在应用层面实现额外的检查。

需要注意的是,ThreadX的互斥锁设计主要用于保护共享资源,确保同一时间只有一个线程可以访问特定资源,并且它支持优先级继承,有助于减少优先级翻转的问题。如果你的应用场景确实需要一个非递归的互斥锁行为,你可能需要自定义一个同步机制,或者考虑使用其他同步原语,如信号量,来实现你的需求。


所以此处应该这样
[C] 纯文本查看 复制代码
void print_uart2(const char *format, ...)
{
    uint16_t len;

    va_list args;
    va_start(args, format);
// 若使用串口自带标志阻塞就没有问题
while (huart2.gState != HAL_UART_STATE_READY)
{
tx_thread_relinquish();
// len = vsnprintf((char*)UartTxBuf,sizeof(UartTxBuf)-1,(char*)format,args);
// 若使用信号量,则即使没有mutexput, 也不会阻塞
   ret = tx_mutex_get(&uart2_mutex, TX_WAIT_FOREVER);
    xsnprintf((char *)&UartTxBuf[0], RX_BUFFER_SIZE_UART1, (const char *)format, args);
    len = strlen((char *)UartTxBuf);
    va_end(args);
    HAL_UART_Transmit_IT(&huart2, UartTxBuf, len);
    ret = tx_mutex_put(&uart2_mutex);
}




回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2024-10-8 10:22:34 | 显示全部楼层
注意,互斥信号量要在任务里面成对调用。保护资源放在get和put之间

你当前的这个非阻塞函数HAL_UART_Transmit_IT没法互斥,你这种的适合用普通信号量,作用是消息同步。但是多任务下调用这个函数print_uart2,依然不太好用。

最好的办法还是互斥信号量,之间调用阻塞的函数HAL_UART_Transmit




回复

使用道具 举报

14

主题

54

回帖

96

积分

初级会员

积分
96
 楼主| 发表于 2024-10-8 10:31:00 | 显示全部楼层
eric2013 发表于 2024-10-8 10:22
注意,互斥信号量要在任务里面成对调用。保护资源放在get和put之间

你当前的这个非阻塞函数HAL_UART_Tra ...

这里有两个问题,
一个是串口永远比cpu慢,HAL_UART_Transmite_IT调用后立即返回,下次调用实际上还没有传完,会覆盖上次的buffer,乱码。所以这里用husart2.gstate判断后使用 tx_thread_relinquish();主动放弃CPU;此处还可以使用HAL_UART_Transmite_DMA
第二个是多线程调用,这里用mutex
回复

使用道具 举报

14

主题

243

回帖

285

积分

高级会员

积分
285
发表于 2024-10-8 14:16:50 | 显示全部楼层
Superusrss 发表于 2024-10-8 10:31
这里有两个问题,
一个是串口永远比cpu慢,HAL_UART_Transmite_IT调用后立即返回,下次调用实际上还没有 ...

我感觉你改完之后,不用mutex也可以,因为你已经判断了串口是否处于busy状态了。
另外你用这种buffer还是用ring buffer比较好,不容易污染。但是我一般是申请内存,用dma发送,dma发送完毕后再释放内存,感觉还是很好用的。
回复

使用道具 举报

14

主题

54

回帖

96

积分

初级会员

积分
96
 楼主| 发表于 2024-10-8 15:25:42 | 显示全部楼层
zhang0352505 发表于 2024-10-8 14:16
我感觉你改完之后,不用mutex也可以,因为你已经判断了串口是否处于busy状态了。
另外你用这种buffer还 ...

这里传入的是线性地址,ringbuffer的话需要处理地址回绕
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2024-10-9 10:09:59 | 显示全部楼层
zhang0352505 发表于 2024-10-8 14:16
我感觉你改完之后,不用mutex也可以,因为你已经判断了串口是否处于busy状态了。
另外你用这种buffer还 ...

不行,你封装的API不支持重入。
比如这个IT后缀和DMA后缀的HAL API,这次没有执行完毕,不能继续执行下次,必须等待执行完毕。

实际上你现在的测试
1、使用轮询 + mutex,这个绝对没问题,也简单
2、使用中断方式 + mutex,这种情况下,在mutex的put和get之间调用串口发送,需要等待传输完成。
3、使用DMA方式+mutex,同上。

实际上2和3,用在RTOS下是合理的,不需要特别处理,死等传输完成就行,因为高优先级的任务此时可以继续执行,完全不影响高优先级任务运行。

回复

使用道具 举报

0

主题

9

回帖

9

积分

新手上路

积分
9
发表于 2024-11-16 18:53:05 | 显示全部楼层
Superusrss 发表于 2024-10-8 07:26
貌似知道原因了,不知道对不对

mutex 在一个线程中多次get是不会阻塞的,其他线程来get才会阻塞。

你这样使用没必要用互斥量了吧
回复

使用道具 举报

0

主题

9

回帖

9

积分

新手上路

积分
9
发表于 2024-11-16 18:54:39 | 显示全部楼层
我的思路与你相同,用法一致,出现的问题也一样,不知道你是否解决了
回复

使用道具 举报

0

主题

9

回帖

9

积分

新手上路

积分
9
发表于 2024-11-16 18:58:42 | 显示全部楼层
eric2013 发表于 2024-10-9 10:09
不行,你封装的API不支持重入。
比如这个IT后缀和DMA后缀的HAL API,这次没有执行完毕,不能继续执行下 ...

3应该具体怎么实现,楼主的思路不就是这样吗?我的测试也是tx_mutex_get不会阻塞
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115434
QQ
发表于 2024-11-17 11:37:45 | 显示全部楼层
buyi 发表于 2024-11-16 18:58
3应该具体怎么实现,楼主的思路不就是这样吗?我的测试也是tx_mutex_get不会阻塞

等待DMA传输完成标志,放在互斥操作内即可。
回复

使用道具 举报

0

主题

9

回帖

9

积分

新手上路

积分
9
发表于 2024-11-19 15:33:54 | 显示全部楼层
eric2013 发表于 2024-11-17 11:37
等待DMA传输完成标志,放在互斥操作内即可。

经过我的反复测试,因为要在dma的isr中释放mutex,使用mutex是不行的,AI回复如下:
在ThreadX实时操作系统中,互斥信号量(mutex)通常用于线程间的同步,以保护共享资源免受并发访问的破坏。然而,中断服务程序(ISR)与线程不同,它们是由硬件事件触发的,并且不遵循ThreadX的调度机制。因此,直接在中断服务程序中使用tx_mutex_put()来释放互斥信号量通常是不被推荐的,甚至可能是不安全的。

原因
上下文切换:中断服务程序可能会打断正在执行的线程,而该线程可能正持有互斥信号量。如果ISR在此时调用tx_mutex_put(),它可能会错误地释放不属于当前中断上下文的互斥信号量,导致资源竞争或死锁。
优先级反转:中断的优先级通常高于线程。如果ISR释放互斥信号量,而另一个低优先级的线程正在等待该信号量,这可能会导致优先级反转问题,即低优先级的线程可能会比高优先级的线程更早地运行。
非确定性行为:由于中断的不可预测性,在ISR中释放互斥信号量可能会导致系统行为变得非确定,从而难以调试和维护。
推荐的解决方案
使用二进制信号量或标志:在中断服务程序中,可以使用二进制信号量(binary semaphore)或标志变量来通知线程某个事件已经发生。然后,在线程中处理该事件,并在适当的时候释放互斥信号量。
延迟处理:在中断服务程序中设置一个标志或计数器,表明有事件需要处理。然后,让线程定期检查这个标志或计数器,并在安全的时候(即持有互斥信号量时)处理事件和释放互斥信号量。
队列或邮箱:如果中断需要向线程传递大量数据,可以使用ThreadX提供的队列(queue)或邮箱(mailbox)机制。这样,中断可以将数据放入队列或邮箱中,而线程则可以在持有互斥信号量时从队列或邮箱中读取数据。
禁用中断:在极端情况下,如果必须在中断服务程序中处理与互斥信号量相关的逻辑,并且可以容忍中断延迟,那么可以考虑在调用tx_mutex_put()之前禁用相关中断。然而,这种方法应该谨慎使用,因为它可能会影响系统的实时性和可靠性。
总之,直接在中断服务程序中使用tx_mutex_put()来释放互斥信号量通常是不安全的。相反,应该使用其他同步机制来在线程和中断之间传递信息,并在线程中安全地处理与互斥信号量相关的逻辑。
回复

使用道具 举报

0

主题

9

回帖

9

积分

新手上路

积分
9
发表于 2024-11-19 15:35:14 | 显示全部楼层
请问为啥回复需要审核后才能显示?这样后台人工岂不是十分忙碌
回复

使用道具 举报

24

主题

131

回帖

203

积分

高级会员

积分
203
发表于 2025-1-5 10:29:03 | 显示全部楼层
单一线程互斥量不起作用,中断和线程之间可以用event做同步。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-4-25 21:50 , Processed in 0.344533 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表