如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
我有两个线程,一个更新一个int,另一个读取它.这是一个统计值,其中读取和写入的顺序无关紧要.
我的问题是,我是否需要同步访问这个多字节值?或者,换句话说,写入的一部分可以完成并被中断,然后读取就会发生.
例如,假设值= 0x0000FFFF,其值递增为0x00010000.
是否有时间值看起来像0x0001FFFF,我应该担心?当然,类型越大,发生这种情况的可能性就越大.
我总是同步这些类型的访问,但很好奇社区的想法.
是C/C++的基本类型,如int,double等,原子,如线程?
他们是否免于数据竞赛; 也就是说,如果一个线程写入这种类型的对象而另一个线程从中读取,那么行为是否定义明确?
如果没有,它是否依赖于编译器或其他东西?
假设我们有两个线程,一个是在循环中读取bool而另一个可以在特定时间切换它.我个人认为这应该是原子的,因为sizeof(bool)在C++中是1个字节而你不是部分读/写字节但我想100%肯定.
那么是或否?
编辑:
另外供将来参考,同样适用于int?
我遇到过C++ 03的一些采用这种形式的代码:
struct Foo {
int a;
int b;
CRITICAL_SECTION cs;
}
// DoFoo::Foo foo_;
void DoFoo::Foolish()
{
if( foo_.a == 4 )
{
PerformSomeTask();
EnterCriticalSection(&foo_.cs);
foo_.b = 7;
LeaveCriticalSection(&foo_.cs);
}
}
Run Code Online (Sandbox Code Playgroud)
读取是否foo_.a需要保护?例如:
void DoFoo::Foolish()
{
EnterCriticalSection(&foo_.cs);
int a = foo_.a;
LeaveCriticalSection(&foo_.cs);
if( a == 4 )
{
PerformSomeTask();
EnterCriticalSection(&foo_.cs);
foo_.b = 7;
LeaveCriticalSection(&foo_.cs);
}
}
Run Code Online (Sandbox Code Playgroud)
如果是这样,为什么?
请假设整数是32位对齐的.该平台是ARM.
通过“原子访问防护”或“中断防护”强制对与 ISR 共享的易失性变量进行原子访问的标准技术,特别是在没有操作系统的情况下运行裸机、单线程协作多任务应用程序时,如下所示:
// 1. save interrupt state
// 2. disable only the interrupts necessary
// You get atomic access to volatile variables shared with ISRs here,
// since ISRs are the only other "context" or running "thread" which
// might attempt to modify a shared memory block or variable.
// 3. restore interrupt state
Run Code Online (Sandbox Code Playgroud)
另请参阅我在这里详细描述的地方,包括最佳实践(在短时间内保持中断关闭)以及如何通过我的doAtomicRead()重复读取循环函数进行原子读取而不首先禁用中断:读取 64 位变量,即由 ISR 更新。
我之前已经记录过如何对 AVR 微控制器/Arduino 执行此操作:How do I Forceatomity in Atmel AVR mcus/Arduino? …
在ARM 文档中,它提到
Cortex-M4 处理器支持 ARMv7 未对齐访问,并将所有访问作为单个未对齐访问执行。它们通过 DCode 和系统总线接口转换为两个或多个对齐的访问。
我不清楚这是否意味着数据访问对程序员来说是原子的。然后我发现了一个 StackOverflow注释,将文档解释为:
实际上,一些 ARM 处理器(例如 Cortex-M3)支持硬件中的未对齐访问,因此即使是未对齐的读/写也是原子的。访问可能跨越多个总线周期到内存,但没有机会让另一条指令在其间跳转,因此对程序员来说是原子的。
不过,我看了看周围多一些,发现索赔违背了先前的要求:
另一个事实是,在 ARMv6 及更高版本的内核上,为了让硬件“修复”未对齐的访问,它会将其拆分为多个较小的字节负载。但是,这些不是原子的!。
那么,我相信谁呢?对于某些情况,我的项目中的打包结构中的每个元素都有 setter/getter 。换句话说,某些结构元素可能未对齐。我想知道在 Cortex-M4 上访问结构元素是否总是保证是原子的。如果不是,我想我将不得不手动启用/禁用中断或添加一些互斥锁,但如果 ARM Cortex M4 可以保证数据访问是原子的,我宁愿不这样做。
以下是STM32微控制器上的数据类型:http : //www.keil.com/support/man/docs/armcc/armcc_chr1359125009502.htm。
这些微控制器使用32位ARM核心处理器。
哪些数据类型具有自动原子读取和原子写入访问权限?
我很确定所有32位数据类型都可以(因为处理器是32位),而所有64位数据类型都不能(因为要读或写一个64位字至少需要2个处理器操作),但是bool(1个字节)和uint16_t/ int16_t(2个字节)呢?
上下文:我正在STM32上的多个线程(在FreeRTOS中称为单核,但有多个线程或称为“任务”)之间共享变量,并且需要知道是否需要通过使用中断关闭中断来强制进行原子访问互斥锁等
更新:
参考此示例代码:
volatile bool shared_bool;
volatile uint8_t shared u8;
volatile uint16_t shared_u16;
volatile uint32_t shared_u32;
volatile uint64_t shared_u64;
volatile float shared_f; // 32-bits
volatile double shared_d; // 64-bits
// Task (thread) 1
while (1)
{
// Write to the values in this thread.
// What I write to each variable will vary. Since other threads
// are reading these values, I need to …Run Code Online (Sandbox Code Playgroud) 在 C++ 中,我们有关键字volatile和atomic类。它们之间的区别是,volatile 不保证线程安全的并发读写,而只是保证编译器不会将变量的值存储在缓存中,而是从内存中加载变量,而 atomic 保证线程安全的并发读写。
众所周知,原子读操作是不可分割的,即当一个或多个线程读取变量的值时,两个线程都不能将新值写入变量,所以我认为我们总是读取最新值,但我不确定:)
所以,我的问题是:如果我们声明原子变量,我们是否总是获得变量调用load()操作的最新值?