代码之家  ›  专栏  ›  技术社区  ›  RobertFrank Picarus

线程可以安全地读取VCL事件设置的变量吗?

  •  5
  • RobertFrank Picarus  · 技术社区  · 16 年前

    线程读取DelphiVCL事件设置的变量是否安全?

    当用户单击VCL TcheckBox时,主线程将布尔值设置为复选框的选中状态。

    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 )

    3 回复  |  直到 16 年前
        1
  •  6
  •   mghie    16 年前

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

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

    • 变量只有一个字节,因此无法读取不一致的值。它将被解读为 True 或作为 False . Delphi不能有对齐问题 boolean 价值观。

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

    尽管如此,如果你真的对此不确定,你可以使用 integer 值而不是布尔值,并使用 InterlockedExchange() 函数写入变量。这在这里是多余的,但这是一种很好的技巧,因为对于单机器字大小的值,它可以消除对锁的需要。

    您也可以替换 布尔 通过一个适当的同步原语,比如一个事件,并在上面设置线程块——这将帮助您消除线程中的繁忙循环。

        2
  •  3
  •   kludg    16 年前

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

    第8章-多处理器管理

    8.1.1保证的原子操作

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

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

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

    • 在64位边界上读取或写入四字
    • 16位访问适合32位数据总线的未缓存内存位置
        3
  •  1
  •   mj2008    16 年前

    举个例子,它是安全的。从技术上讲,主要的问题是当变量超过机器字的大小时,可能会导致高位字和长字不同步。但对于小值而言,这不是问题。

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