RT-Thread论坛
直播中

李敏

7年用户 1345经验值
私信 关注
[问答]

sem信号量下semxx->parent.suspend_thread->next的值,应该执行take线程的被改变成了release线程,为什么?

在使用信号量时,一个线程release一个线程take,在某些特定时候,take线程永远不执行了,release线程确认一直有释放,sem下面的Value一直在增加,

详细追踪,发现在take的某个时候,sem信号量下的semxx->parent.suspend_thread->next的值发生了改变,本来应该指向take线程的地址,变成了指向release线程地址,导致sem下的supend_thread列表在release时恢复take线程.
代码如下

#include "outunit.h"
#include "app.h"
#include "uhp.h"
//ALIGN(RT_ALIGN_SIZE)
#define     OUTUNIT_UART_NAME  "uart2"
#define      PARSE_DATA_HEAD         0
#define      PARSE_DATA_BODY         1
#define      PARSE_DATA_TAIL          2   
#define      PARSE_DATA_CHECK     3
#define     DATA_LEN     19
rt_uint8_t  frame_buf[20];
rt_uint8_t  state = 0;
rt_uint8_t  frame_buf_offset = 0;
rt_uint8_t  outUnitConnectErr;
rt_uint8_t    *shiyuan_setHandler;
typedef   struct  _shiyuan_format{
    uint8_t  head;
    uint8_t  addr;
    uint8_t  function;
    uint8_t  len;
    uint8_t  data[13];
    uint8_t  tail;
    uint8_t  verify;
}shiyuan_format, *shiyuan_format_t;
struct  rx_msg{
    rt_device_t      dev;     // 设备句柄
    rt_size_t              receiveSize; // 接收数据尺寸
};
const char  *uart_name;  // 设备名字
struct   rt_semaphore        outUnitTxSem; //发送信号量
struct   rt_messagequeue  outUnitRxMq; // 消息队列
rt_device_t   outUnitdev;
typedef struct {
    SHIYUAN_OUTINFO  sy_data;
    SHIYUAN_OUTSET   sy_set;
} OUT_UNIT;
OUT_UNIT outUnit;
/*
* 函数名称: SUM_Check()
*/
rt_uint8_t SUM_Check(rt_uint8_t *buf, rt_uint8_t len)
{
    rt_uint8_t temp_result = 0, cnt = 0;
    for (cnt = 0; cnt < len; cnt++)
    {
        temp_result += buf[cnt];
    }
    temp_result = ~temp_result;
    return temp_result;
}
// 发送编码
static  void  OutUnit_ReplyCode(rt_uint8_t  *tx_buf ,  rt_uint8_t com_len )
{
    rt_device_write(outUnitdev,0, tx_buf, com_len);
}
void   shiyuan_send(void )
{
    static shiyuan_format  sy_dev;
    sy_dev.head = 0xAA;
    sy_dev.addr =frame_buf[1];
    sy_dev.len   =13;
    sy_dev.function =frame_buf[2]+0x30;
    switch(frame_buf[2])
    {
        case 0x11:   
            rt_memcpy(sy_dev.data,                     (uint8_t*)&outUnit.sy_set.curIndoorMode,sy_dev.len); // 如果是41信息回复
            sy_dev.data[sy_dev.len-1]= 0x06;   // 湿度设置 50   // 最后一个字节是设置湿度
            break;
        case 0x12:
             rt_memcpy(sy_dev.data,(uint8_t*)&outUnit.sy_set.curIndoorMode,sy_dev.len);
            break;
        case 0x13:
        case 0x14:
             rt_memset((uint8_t*)sy_dev.data,0,sy_dev.len);
            break;
    }
    sy_dev.tail =0xBB;
    sy_dev.verify = SUM_Check(&sy_dev.addr, DATA_LEN- 2);            
    OutUnit_ReplyCode((uint8_t*)&sy_dev.head,DATA_LEN);  // 发送数据
}
//发送处理线程
static void OutUnit_TxThreadEntry(void *parameter)
{
    (void ) *parameter;
    while(1)
    {
        if( RT_EOK == rt_sem_take(&outUnitTxSem,RT_WAITING_FOREVER))// 等待信号量释放
        {
            shiyuan_send();
        }
        else
        {
            rt_kprintf("out tx sem take Error!\n");
        }
    }
}
/********************************************************************************/
// 接收回调函数
rt_err_t OutUnit_ReceiveCb(rt_device_t dev, rt_size_t size)
{
    rt_err_t ret =0;
    struct  rx_msg  msg;
    msg.dev = dev;
    msg.receiveSize  = size>DATA_LEN ? DATA_LEN : size;
    ret = rt_mq_send(&outUnitRxMq,&msg, sizeof(struct rx_msg));  //发送消息队列
    return ret;
}
// 字节解析
static  void  OutUnit_ReceiveDecodeChar(rt_uint8_t  ch )
{
    rt_uint8_t *temp_ptr = 0;
    switch(state)
    {
        case  PARSE_DATA_HEAD:
                if (frame_buf_offset ==0)// 刚解析第一个
                {
                    if(ch == 0xAA)
                    {
                        frame_buf[0] = ch;
                        frame_buf_offset =1;
                    }
                }
                else if(frame_buf_offset == 1) // 解析第二个
                {
                    if (ch ==0x01)
                    {
                        frame_buf[1] = ch;
                        frame_buf_offset =2;
                    }
                }
                else if (frame_buf_offset ==2) //  第三字节
                {
                    if ((ch<=0x14)&&(ch>=0x11))
                    {
                        frame_buf[2] = ch;
                        frame_buf_offset =3;
                    }
                }
                else if (frame_buf_offset ==3) //  第三字节
                {
                    if (ch==13)
                    {
                        frame_buf[3] = ch;
                        frame_buf_offset =4;
                        state = PARSE_DATA_BODY;
                    }
                }
                else
                {
                    frame_buf_offset =0;
                    state = PARSE_DATA_HEAD;
                }
        break;
        case  PARSE_DATA_BODY:        
                frame_buf[frame_buf_offset] =ch;  // 存储每个数据
                frame_buf_offset++;
                if (frame_buf_offset > DATA_LEN-3)  
                {
                    state = PARSE_DATA_TAIL;
                }
        break;
        case  PARSE_DATA_TAIL:        
                if (ch == 0xBB)  
                {
                    frame_buf[frame_buf_offset++]=ch;
                    state = PARSE_DATA_CHECK;
                }
        break;
        case  PARSE_DATA_CHECK:        
                if (SUM_Check(&frame_buf[1], DATA_LEN- 2) ==ch)
                {
                    timeCnt.oduConnectCnt=0;
                    frame_buf[frame_buf_offset]=ch;
                    if (frame_buf[2]== 0x11)
                        temp_ptr = &outUnit.sy_data.r0x11_curOutRunMode;
                    else if (frame_buf[2]== 0x12)
                        temp_ptr = &outUnit.sy_data.r0x12_acCurrent_H;
                    else if (frame_buf[2]== 0x13)
                        temp_ptr = &outUnit.sy_data.r0x13_codeIndex_HH;
                    else if (frame_buf[2]== 0x14)
                        temp_ptr = &outUnit.sy_data.r0x14_ctrlIndoorEnable1;
                    rt_memcpy(temp_ptr,(rt_uint8_t*)(frame_buf +4),DATA_LEN- 6);
                    rt_sem_release(&outUnitTxSem); // 释放一个发送信号量 ,怀疑释
                }
                frame_buf_offset=0;
                state = PARSE_DATA_HEAD;
        break;        
    }
}
#endif /* 1 */
//接收处理线程
static void OutUnit_RxThreadEntry(void *parameter)
{
     struct  rx_msg  msg;
     rt_size_t len=0;
     rt_uint8_t rx_buf[RT_SERIAL_RB_BUFSZ ]={0};
     (void ) *parameter;
       while(1)
    {
         rt_memset(&msg, 0, sizeof(msg));
        if ( RT_EOK == rt_mq_recv(&outUnitRxMq, &msg, sizeof(msg), RT_WAITING_FOREVER)) //获取 消息队列 移植等待
        {
                len = rt_device_read(msg.dev,0, &rx_buf,msg.receiveSize);  //  
                for(uint8_t cnt=0; cnt< len ;cnt++)
                {
                    OutUnit_ReceiveDecodeChar(rx_buf[cnt]);
                }
        }
    }
}
// 初始化函数
static int OutUnit_Init(void)
{
    static  char msg_pool[256];
    rt_memset(&outUnit.sy_data,0,sizeof(SHIYUAN_OUTINFO));
    rt_memset(&outUnit.sy_set,0,sizeof(SHIYUAN_OUTSET));
    rt_sem_init(&outUnitTxSem,"outtx",0,RT_IPC_FLAG_FIFO); // 创建发送信号量
    rt_mq_init(&outUnitRxMq,"outrx",msg_pool,sizeof(struct rx_msg),sizeof(msg_pool),RT_IPC_FLAG_FIFO);   //
    uart_name = OUTUNIT_UART_NAME ;
    static struct  serial_configure  cfg = {
        .baud_rate = 600,
        .data_bits = DATA_BITS_8,
        .stop_bits = STOP_BITS_2,
        .bit_order=BIT_ORDER_LSB,
        .bufsz = 256,
        .parity = PARITY_EVEN,   // 偶校验
    };
    outUnitdev = rt_device_find(uart_name);// 获取设备
    if (outUnitdev != RT_NULL)
        rt_device_control(outUnitdev,RT_DEVICE_CTRL_CONFIG,&cfg);  // 初始化串口
    rt_device_open(outUnitdev,RT_DEVICE_FLAG_DMA_RX|RT_DEVICE_FLAG_DMA_TX);  // DMA读写打开
    rt_device_set_rx_indicate(outUnitdev,  OutUnit_ReceiveCb); // 设置接收回到函数
    rt_thread_t rx_thread = rt_thread_create("rx", OutUnit_RxThreadEntry, RT_NULL, 1024, 12, 10);
    RT_ASSERT(rx_thread != RT_NULL);
    rt_thread_startup(rx_thread);
    rt_thread_t tx_thread = rt_thread_create("tx", OutUnit_TxThreadEntry, RT_NULL, 1024, 9,20);
    RT_ASSERT(tx_thread != RT_NULL);
    rt_thread_startup(tx_thread);
    return 0;
}
INIT_APP_EXPORT(OutUnit_Init);
/***************************************************************************************/
// 读数据
void* OutUnit_GetOutUnitData(void)
{
    return (SHIYUAN_OUTINFO*)&outUnit.sy_data;
}
//  写入更新数据
int  OutUnit_WriteOutUnitData(rt_uint8_t *rData , rt_uint16_t offset, rt_uint16_t num)
{
    if(offset +num >sizeof(SHIYUAN_OUTSET)) //超出范围
        return -1;
     rt_memcpy((rt_uint8_t*)(&outUnit.sy_set.curIndoorMode+offset),  rData , num);
    return num;
}

回帖(1)

郭大

2024-7-13 17:21:35
从您提供的代码片段和描述来看,这个问题可能是由于信号量实现中的同步问题或者竞争条件导致的。在多线程环境中,当多个线程同时访问共享资源(如信号量)时,如果没有适当的同步机制,就可能出现数据不一致的问题。

在您的情况下,sem信号量下的semxx->parent.suspend_thread->next的值发生了改变,可能是因为在take线程和release线程之间存在竞争条件。当两个线程同时访问这个值时,可能会导致其中一个线程的操作被覆盖,从而导致问题。

为了解决这个问题,您可以尝试以下方法:

1. 使用互斥锁(mutex)或其他同步机制来保护信号量的访问。这样可以确保在任何时刻,只有一个线程可以访问信号量。

```c
#include

pthread_mutex_t sem_mutex = PTHREAD_MUTEX_INITIALIZER;

// 在访问信号量之前加锁
pthread_mutex_lock(&sem_mutex);

// 访问信号量

// 访问完成后解锁
pthread_mutex_unlock(&sem_mutex);
```

2. 检查信号量的实现,确保它在多线程环境下是安全的。如果可能的话,使用已经经过测试和验证的信号量实现,如POSIX线程库中的semaphore。

3. 在调试过程中,使用工具(如gdb)或日志记录来跟踪信号量的访问和修改,以便更好地了解问题发生的原因。

4. 考虑使用条件变量(condition variable)来实现线程间的同步。条件变量可以更好地处理线程间的等待和通知机制,从而避免竞争条件。

请注意,由于您提供的代码片段不完整,以上建议可能需要根据您的具体情况进行调整。希望这些建议能帮助您解决问题。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分