我在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;
}
答案 0 :(得分:9)
正如你所记录的那样,这不是原子的。但是在严格意义上它仍然可以是“线程安全的”:long
不能处于不一致状态。这里的危险程度是,如果n
个线程调用vTaskSuspendAll
,则uxSchedulerSuspended
将增加1到n
之间的任何位置。
但如果变量不需要完美,那么这可能非常好,就像用户要求暂停多少次的跟踪器一样。有“线程安全”的意思是“这个操作产生相同的结果,无论它的调用是如何交错的”,并且“线程安全”意味着“如果你从多个线程调用它,则不会爆炸”。
答案 1 :(得分:8)
不,C中的递增值不保证是原子的。您需要提供同步,或使用系统特定的库来执行原子递增/递减。
答案 2 :(得分:6)
该操作不是原子操作,但它没有说它是。但是,代码是线程安全的,但您必须非常熟悉代码正在做什么,以及它如何适应调度程序的设计以了解它。其他任务是否修改了加载和存储之间的变量并不重要,因为当执行任务下次运行时,它将找到与执行原始加载时相同状态的变量(因此修改和写入部分仍然一致且有效)。
如前所述,long不能处于不一致状态,因为它是运行它的体系结构的基本类型。但是请考虑如果代码在8位机器(或16位)上运行并且变量为32位会发生什么。然后它将不是线程安全的,因为一次完整的32位将被修改为字节或字,而不是一次修改。在这种情况下,一个字节可能被加载到一个寄存器中,经过修改,然后在发生上下文切换时写回RAM(其他三个字节保持不变)。如果执行的下一个任务读取相同的变量,它将读取一个已修改的字节和三个没有的字节 - 并且您有一个主要问题。