线程读取由Delphi VCL事件设置的变量是否安全?
当用户点击VCL TCheckbox时,主线程会将一个布尔值设置为复选框的Checked状态。
CheckboxState := CheckBox1.Checked;
任何时候,线程都会读取该变量
if CheckBoxState then ...
如果线程“错过”对布尔值的更改,则无关紧要,因为线程会检查循环中的变量,因为它会执行其他操作。所以最终会看到状态变化......
这样安全吗?或者我需要特殊代码?围绕变量的读写(分别在线程和主线程中)是否需要充分的关键代码调用?
正如我所说的,如果线程获得“错误”值并不重要,但我一直认为如果一个线程在主线程位于其中时尝试读取变量,则可能存在低级问题写作的中间,反之亦然。
我的问题与此类似:Cross thread reading of a variable who's value is not considered important。
(也与我之前的问题相关:Using EnterCriticalSection in Thread to update VCL label)
答案 0 :(得分:6)
这是安全的,原因有三:
只有一个线程写入变量。
变量只有一个字节,因此无法读取不一致的值。它将被视为True
或False
。 Delphi boolean
值不存在对齐问题。
Delphi编译器不会对变量是否实际写入进行广泛检查,如果没有,则不会“优化”任何代码。将始终读取非局部变量,不需要volatile
说明符。
话虽如此,如果您对此确实不确定,可以使用integer
值而不是布尔值,并使用InterlockedExchange()
函数写入变量。这在这方面有点过头了,但这是一个很好的技术,因为对于单机字大小的值,它可以消除锁的需要。
您还可以通过适当的同步原语(如事件)替换boolean
,并在其上设置线程块 - 这将有助于消除线程中的繁忙循环。
答案 1 :(得分:3)
在您的情况下(Checked属性),读取操作是原子的,因此它是安全的。这与TThread.Terminated属性相同;正确对齐的字节,单词和双字的简单读写操作是原子的。您可以查看intel documentation以获取更多信息:
第8章 - 多处理器管理
8.1.1保证原子操作
Intel486处理器(以及更新的处理器)保证以下内容 基本的内存操作将始终以原子方式执行:
Pentium处理器(以及更新的处理器)保证以下内容 额外的内存操作将始终以原子方式执行:
答案 2 :(得分:1)
对于您给出的示例,它将是安全的。从技术上讲,主要问题是你的变量超过机器字大小,因此你可能会得到高字和长字不同步。但是对于较小的值,这不是问题。
如果您认为这是一个潜在的问题,例如使用指针,那么要使用的是TCriticalSection来控制对项目的读取和写入。这对于所有实际情况都足够快,并确保您100%安全。