16

我在 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”,它是“长”类型。我的理解是它假设这种类型的自增是原子的。但是在我拆开它之后我找不到任何证据,它是一个简单的加载->添加->存储。那么有问题吗?

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;
}
4

3 回答 3

9

正如您所记录的,它不是原子的。但在不太严格的意义上它仍然可以是“线程安全的”:along不能处于不一致的状态。这里的危险程度是,如果n线程调用vTaskSuspendAllthenuxSchedulerSuspended将增加 1 到n.

但是,如果变量不需要完美的话,这可能会很好,比如用户要求暂停多少次的跟踪器。有“线程安全”的意思是“这个操作产生相同的结果,不管它的调用是如何交错的”,还有“线程安全”的意思是“如果你从多个线程调用它不会爆炸”。

于 2013-03-20T01:55:50.303 回答
8

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

于 2013-03-20T01:24:17.817 回答
6

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

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

于 2013-03-20T09:33:27.740 回答