【问题标题】:Can threads safely read variables set by VCL events?线程可以安全地读取 VCL 事件设置的变量吗?
【发布时间】:2010-04-08 14:23:06
【问题描述】:

线程读取 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

【问题讨论】:

  • 请注意,VCL 事件并没有什么特别之处。您的问题同样适用于从 any 两个线程中的 any 类型的函数访问变量。

标签: multithreading delphi


【解决方案1】:

这是安全的,有三个原因:

  • 只有一个线程写入变量。

  • 变量只有一个字节,所以没有办法读取不一致的值。它将被读取为TrueFalse。 Delphi boolean 值不会存在对齐问题。

  • Delphi 编译器不会广泛检查变量是否实际写入,如果没有,也不会“优化”掉任何代码。将始终读取非局部变量,不需要 volatile 说明符。

话虽如此,如果您对此不确定,可以使用integer 值代替布尔值,并使用InterlockedExchange() 函数写入变量。这在这里有点矫枉过正,但这是一个很好的技术,因为对于单个机器字大小的值,它可以消除对锁的需求。

您还可以将boolean 替换为适当的同步原语,如事件,并在其上设置线程块 - 这将帮助您消除线程中的繁忙循环。

【讨论】:

    【解决方案2】:

    在您的情况下(检查属性),读取操作是原子的,因此它是安全的。这与 TThread.Terminated 属性相同;正确对齐的字节、字和双字的简单读写操作是原子的。您可以查看intel documentation了解更多信息:

    第 8 章 - 多处理器管理

    8.1.1 保证原子操作

    Intel486 处理器(以及之后的更新处理器)保证以下 基本的内存操作将始终以原子方式执行:

    • 读取或写入一个字节
    • 读取或写入在 16 位边界上对齐的字
    • 读取或写入在 32 位边界上对齐的双字

    奔腾处理器(以及之后的更新处理器)保证以下 额外的内存操作将始终以原子方式执行:

    • 读取或写入在 64 位边界上对齐的四字
    • 对适合 32 位数据总线的未缓存内存位置进行 16 位访问

    【讨论】:

      【解决方案3】:

      对于您给出的示例,它将是安全的。从技术上讲,主要问题是您的变量在大小上超过机器字的情况下,因此您可能会得到高字和长字不同步的情况。不过,对于较小的值,这不是问题。

      如果您认为这是一个潜在问题,例如使用指针,那么要使用 TCriticalSection 来控制对项目的读取和写入。这对于所有实际情况都足够快,并确保您 100% 安全。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-24
        • 2016-07-13
        • 2013-04-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 1970-01-01
        • 2015-04-03
        相关资源
        最近更新 更多