关于何时应该使用关键字“volatile”?
关于何时使用关键字'volatile'的问题是因为以下原因而出现的:
在多线程的情况下,一个线程可能会修改另一个线程中的变量。然而,编译器无法知道某个变量是否会被其他线程修改,因此它可能会对代码进行优化,导致出现错误。
解决这个问题的方法是使用关键字'volatile'来声明变量。这样,编译器就知道这个变量可能会被其他线程修改,从而避免了优化错误。
在上面的示例中,一个线程通过设置标志位来通知另一个线程发生了中断。如果没有使用'volatile'关键字,主线程可能无法及时获取到这个通知。
因此,如果在同一个项目中的代码中,某个变量将被其他线程或中断服务程序修改,就需要使用'volatile'关键字。然而,如果这个变量只会被同一个线程中的其他函数修改,就不需要使用'volatile'关键字。
这可能对我来说不太合理。当代码嵌套在一起时,编译器如何知道某些代码是否属于同一个线程呢?实际上,编译器假设所有可能的变量都可以被修改。如果有一段代码没有明确地指示变量被修改,编译器会假设这个变量可能被修改。
然而,如果编译器使用了链接时优化,它可以知道所有函数的行为,从而知道哪个变量被修改。
需要注意的是,'volatile'关键字并不能解决并发访问的问题,仅仅是告诉编译器某个变量可能会被其他线程修改。
因此,在处理多线程的情况下,除了使用'volatile'关键字外,还需要进行适当的同步操作来保护变量免受并发访问的影响。
在C语言中,关键字volatile
告诉编译器不要做任何假设,也不要做任何“捷径”,而是要做所有的工作。这意味着变量始终从内存中读取和写入,而不是保存在寄存器中。编译器还将忽略局部上下文对变量可能暗示的一切,并且放弃进行某些优化。
无论是哪个文件或哪个函数,只要在程序中有可能变量的值从多个地方改变(甚至是另一个进程或其他硬件机制),就应该将其标记为volatile
,以便从其实际位置检索正确的值,而不是使用假定的值或寄存器中的缓存副本,或者完全被优化掉。
还要注意,对volatile
变量的操作不是原子的。在多线程场景中应使用原子操作。
简而言之,volatile
主要用于低级硬件操作,通常是当除了您的代码之外的其他东西可能会更改变量时使用,它几乎不会被用于应用程序开发。它主要用于微控制器/硬件驱动程序使用。
我不太明白你说的。许多变量将从程序中的多个点进行更改(例如全局变量),但其中许多变量不需要标记为'volatile'。
编译器默认会尽可能地限制对内存的访问,这意味着普通变量将被缓存在寄存器中,从该寄存器中使用和修改,并且当函数完成时,其值将在内存中更新。因此,中间更改不会反映在内存中该变量的实际位置。这意味着如果另一个函数访问该变量,它将访问可能是来自内存的过时副本。如果变量是volatile,每个操作都将针对实际的内存位置进行,从另一个点访问它将是最新的。
对于全局变量,编译器可以执行静态分析并使用正确的访问方式。volatile
指示编译器在编译时不对可能不明确的情况做任何假设。正如其他人所指出的,除非您正在进行内存映射、处理硬件中断和其他高级操作,否则通常不需要使用volatile
。
非常感谢您的答复。所以,我们不要假设内存中的某个位置将被硬件更改。让我们假设所有的更改都来自软件(在不同的线程中更改,在中断处理程序中更改)。根据您的说法,编译器应该完全知道变量是否会在其他地方更改。在这种情况下,就没有需要使用volatile的情况了。感到困惑....
- 在大多数情况下,这是正确的。或者在非常罕见的情况下,编译器可能无法深入分析并进行一些不反映副作用的优化,这将导致一个bug,您可以通过将变量标记为volatile来解决,但这种情况极其罕见。在99.99%的情况下,您不应该使用volatile,而应该使用互斥锁来防止竞争条件。请注意,中断处理程序可能确实需要volatile,但这取决于使用场景以及外部程序是否存在更改的可能性或它无法预测的变化。
关于何时使用关键字'volatile'的问题,出现的原因是为了确保在多线程环境下正确访问内存块。解决方法是使用关键字'volatile'来告诉编译器不要尝试缓存该变量,包括硬件修改内存和多线程访问相同内存的情况。
在多线程情况下,建议使用互斥锁或其他可用的同步工具来处理。无论代码是否在同一个文件中,重要的是执行代码的线程。
除非你从事设备驱动程序的业务,否则几乎不需要在代码中使用'volatile'关键字。
'volatile'不仅仅是告诉编译器不要尝试缓存。编译器有很多行为是不允许做的,而有些行为是必须做的。例如,对'volatile'变量的读写永远不会被消除,因为编译器必须假设它具有副作用。
,在硬件编程中,当变量由同一项目中的软件更改时,可能属于同一个或不同的线程,需要使用'volatile'关键字来确保正确访问内存块。