C线程中的自增量是否安全?

时间:2013-03-20 01:20:49

标签: c freertos

我在FreeRTOS(FreeRTOSV7.4.0 \ FreeRTOS \ Source \ tasks.c)中找到了一些代码:

void vTaskSuspendAll( void )
{
    /* A critical section is not required as the variable is of type
    portBASE_TYPE. */
    ++uxSchedulerSuspended;
}

明确表示不需要保护,因为类型是“portBASE_TYPE”,这是一种“长”类型。我的理解是它假设这种类型的自增量是原子的。但在我拆开它之后,我找不到任何证据,它是一个简单的加载 - > add->存储。那是一个问题吗?

void vTaskSuspendAll( void )
{
        /* A critical section is not required as the variable is of type
        portBASE_TYPE. */
        ++uxSchedulerSuspended;
 4dc:   4b03            ldr     r3, [pc, #12]   ; (4ec <vTaskSuspendAll+0x10>)
 4de:   f8d3 2118       ldr.w   r2, [r3, #280]  ; 0x118
 4e2:   1c50            adds    r0, r2, #1
 4e4:   f8c3 0118       str.w   r0, [r3, #280]  ; 0x118
 4e8:   4770            bx      lr
 4ea:   bf00            nop
 4ec:   00000000        .word   0x00000000

000004f0 <xTaskGetTickCount>:
        return xAlreadyYielded;
}

3 个答案:

答案 0 :(得分:9)

正如你所记录的那样,这不是原子的。但是在严格意义上它仍然可以是“线程安全的”:long不能处于不一致状态。这里的危险程度是,如果n个线程调用vTaskSuspendAll,则uxSchedulerSuspended将增加1到n之间的任何位置。

但如果变量不需要完美,那么这可能非常好,就像用户要求暂停多少次的跟踪器一样。有“线程安全”的意思是“这个操作产生相同的结果,无论它的调用是如何交错的”,并且“线程安全”意味着“如果你从多个线程调用它,则不会爆炸”。

答案 1 :(得分:8)

不,C中的递增值不保证是原子的。您需要提供同步,或使用系统特定的库来执行原子递增/递减。

答案 2 :(得分:6)

该操作不是原子操作,但它没有说它是。但是,代码是线程安全的,但您必须非常熟悉代码正在做什么,以及它如何适应调度程序的设计以了解它。其他任务是否修改了加载和存储之间的变量并不重要,因为当执行任务下次运行时,它将找到与执行原始加载时相同状态的变量(因此修改和写入部分仍然一致且有效)。

如前所述,long不能处于不一致状态,因为它是运行它的体系结构的基本类型。但是请考虑如果代码在8位机器(或16位)上运行并且变量为32位会发生什么。然后它将不是线程安全的,因为一次完整的32位将被修改为字节或字,而不是一次修改。在这种情况下,一个字节可能被加载到一个寄存器中,经过修改,然后在发生上下文切换时写回RAM(其他三个字节保持不变)。如果执行的下一个任务读取相同的变量,它将读取一个已修改的字节和三个没有的字节 - 并且您有一个主要问题。