[讨论] 请教任务切换问题

black0936   2012-6-13 21:39 楼主
我之前也没有用过操作系统,刚刚接触uC,想请教诸位一个问题。
以下是我写的一段代码,核心思想是在一个任务运行过程中检查变量,如果发现超出阈值则挂起此任务,
唤醒另外一个任务。但是事实上每次都无法切换任务,仍然在执行原任务。可能是什么原因呢?
while(1)
    {
        if(auto_speed <= 5)
        {
            Uart_SendString("Swtich WorkMode\n");                                                     (1)
            err = OSTaskSuspend(OS_PRIO_SELF);//挂起当前任务--自动巡航
            Uart_ShowInt(err);                                                                                       (2)
            Uart_SendByte('\t');                                                                                     (3)
            err = OSTaskResume(StopModeTaskPrio);//手动驾驶任务解除挂起
            Uart_ShowInt(err);
            Uart_SendByte('\n');
            break;       
        }
        OSTimeDly(OS_TICKS_PER_SEC/50);//频率为50Hz
    }
我总共有三个任务,StopModeTask、ManualModeTask、AutoModeTask,刚开始时都被挂起,先运行AutoModeTask,按键中断
可以改变速度,如果速度小到一定程度,就挂起当前的AutoModeTask任务,转而执行StopModeTask任务。
实际的运行结果是输出来了(1)处,但是(2)(3)处都没有输出,并且一直在原任务——AutoModeTask运行,根本没有切换。
请问这是什么原因呢?

回复评论 (6)

回复 楼主 black0936 的帖子

err = OSTaskSuspend(OS_PRIO_SELF);//挂起当前任务--自动巡航
当这条语句被执行结束之后,任务AutoModeTask即被挂起,后续的代码不被执行(也就是说StopModeTask这个任务是无法被激活的,而2、3也不会输出),由于此时你的其他两个任务StopModeTask、ManualModeTask也都处于被挂起的状态。因此,此时系统实际上是在运行空闲任务或者统计任务。
这是根据你给出的代码所做的推测,因为不清楚你其他部分的代码是怎么写的,所以仅供参考。
点赞  2012-6-15 19:18
非常感谢juntianya的回复。本来已经隔了几天没看这个论坛了,没想到您还是热心的帮我解答问题,非常感谢。事实上也的确是你说的那个问题,我颠倒了一下顺序就好了。非常感谢。
现在还是有一个问题希望请教。我是三个任务来回切换,例如说A、B、C,切换有两种可能,中断或者是任务中设置条件满足。现在问题来了,有这种情况,在任务B运行过程中(此时A、C都没有运行)
    if(满足条件)
        {
            OSTaskResume(A);//解除任务A的挂起状态
            OSTaskSuspend(B);//挂起当前任务B
        }
这种情况下,解除A的挂起状态时运行过OSSched(),因为A的优先级高,所以直接切换过去运行A,等到A中运行到OSTimeDly()这一块,又转回来运行B,然后发现B需要挂起,有点绕远路的感觉。我觉得这个过程有点麻烦,有没有什么方法能先解除A的挂起,再挂起B,然后才运行调度函数,进行任务切换。我的想法是
            if(满足条件)
            {
                        OSLockNesting++;
                        OSTaskResume(A);//解除任务A的挂起状态            
                        OSLockNesting++;
                        OSTaskSuspend(B);//挂起当前任务B
            }
这样子就能保证先解除A的挂起状态同时不进行任务切换,留到挂起B的时候再进行任务切换,你觉得这样合适吗?谢谢!
点赞  2012-6-17 11:05

回复 板凳 black0936 的帖子

额……第二句 OSLockNesting++;应该是OSLockNesting--;吧?
这样做可以。
不过直接对OSLockNesting自增和自减而不做任何的保护是有一定风险的,会影响系统的稳定性。你可以用OSSchedLock ()和OSSchedUnlock()来代替,考虑到OSSchedUnlock()中会进行任务调度的问题,可以把这句放到 OSTaskSuspend(B)之后。
又或者至少使用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来对OSLockNesting自增和自减做保护。
当然以上的两种做法主要是考虑到系统的稳定性问题。如果你的程序本身比较简单的话,就无关紧要了,直接按你自己说的来就行了。恕我啰嗦了,呵呵……
点赞  2012-6-17 13:15
其实想问的正是这一句,如果将OSSchedUnlock()放到OSTaskSuspend(B)之后,那这一句还会不会执行呢?挂起任务后是不是之后的语句都不会执行了呢?也就是说,实际上OSTaskSuspend(B)挂起自身后又没有来得及执行OSSchedUnlock(),所以程序无事可做,可能会死掉。这样理解对吗?谢谢指点
点赞  2012-6-17 14:47

回复 5楼 black0936 的帖子

OSTaskSuspend(B),这句话的意思是将B任务挂起。但是,这个函数时如何完成对于任务的挂起的呢?说白了,就是两步。第一步:将这个任务置为OS_STAT_SUSPEND(挂起状态),这样做可以保证任务在未被恢复之前将不会被涉及到OS的任务调度中去。第二步:调用OS_Sched()函数,进行任务切换。
而在我们现在讨论的场景之下,第一步被执行了,但是第二步却被阻止了。于是,由于没有执行任务调度,占用CPU的还是任务B,OSTaskSuspend(B)函数执行完成以后又返回到了任务B当中执行下一条代码,也就是OSSchedUnlock()函数,完成了调度开锁和任务调度操作。
说白了,就是将挂起函数的第二步拖延到调度开锁以后再执行。
点赞  2012-6-17 15:34
精辟,实在感谢。看问题果然要耐心细致,非常感谢。
点赞  2012-6-17 16:30
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复