10

'布尔'变量对于从任何线程读取和写入是线程安全的吗?我已经看到一些新闻组引用说它们是。是否有任何其他数据类型可用?(枚举类型,也许是短整数?)

最好有一个可以从任何线程安全读取的所有数据类型的列表,以及另一个可以在任何线程中安全写入的列表,而无需求助于各种同步方法。

4

5 回答 5

9

请注意,您基本上可以使 delphi 中的所有内容都成为非线程安全的。虽然其他人提到布尔值的对齐问题,但这在某种程度上隐藏了真正的问题。

是的,如果正确对齐,您可以在任何线程中读取布尔值并写入任何线程中的布尔值。但是从你改变的布尔值中读取并不一定是“线程安全的”。假设您在更新数字时将布尔值设置为 true,以便另一个线程读取该数字。

if NumberUpdated then
begin
  LocalNumber = TheNumber;
end;

由于优化,处理器使 TheNumber 可能在 NumberUpdated 被读取之前被读取,因此即使您最后更新 NumberUpdated,您也可能获得 TheNumber 的旧值。

又名,您的代码可能变为:

temp = TheNumber;
if NumberUpdated the
begin
  LocalNumber = temp;
end;

恕我直言,一个基本的经验法则:
“读取是线程安全的。写入不是线程安全的。”
因此,如果您要对数据进行写保护,并在您读取值的任何地方进行同步,同时可能会发生写入。
另一方面,如果你只在一个线程中读取和写入一个值,那么它是线程安全的。因此,您可以在临时位置进行大量写入,然后同步更新应用程序范围的数据。

奖金简介:

VCL 不是线程安全的。将所有 ui 内容的修改保留在主线程中。在主线程中保留所有 ui 内容的创建。

许多函数也不是线程安全的,而其他函数则通常取决于底层的 winapi 调用。

我认为“列表”不会有帮助,因为“线程安全”可能意味着很多东西。

于 2009-02-04T11:49:57.137 回答
8

这不是数据类型是否线程安全的问题,而是您如何处理它们的问题。没有锁定的任何操作都是线程安全的,包括加载一个值,然后更改它,然后将其写回:递增或递减一个数字,清除或设置集合中的一个元素——它们都不是线程安全的。

有许多允许原子操作的函数:互锁递增、互锁递减和互锁交换。这是一个常见的概念,与 Windows、x86 或 Delphi 无关。对于 Delphi,您可以使用 Windows API 的 InterlockedFoo() 函数,这些函数也有几个包装器。或者自己写。这些函数对整数进行操作,因此您可以与它们进行整数(32 位)的原子递增、递减和交换。

您还可以使用带有锁定前缀的汇编程序和前缀操作。

有关更多信息,另请参阅此 StackOverflow问题。

于 2009-02-04T06:51:31.747 回答
4

在 32 位架构上,只有正确对齐的 32 位或更少的数据类型才应被视为原子数据类型。32 位值必须是 4 对齐的(数据的地址必须能被 4 整除)。您可能不会在如此紧密的级别上遇到交错,但理论上您可以进行双重、Int64 或扩展的非原子写入。

于 2009-02-04T06:33:22.827 回答
3

由于现代处理器混合了多核 RISC 处理和独立的核心高速缓存,任何“微不足道”的高级语言读取或写入构造(或就此而言许多曾经- a-time 8086 'atomic' 汇编指令)可以被认为是原子的。事实上,除非汇编指令是专门设计为原子的,否则它可能不是原子的——这包括大多数内存读取机制。即使是汇编程序级别的长整数读取也可能被来自共享相同内存的另一个处理器内核的同时写入破坏,并在 RISC 处理器级别使用异步缓存更新操作。请记住,在包含多个 RISC 内核的处理器上,即使是汇编语言指令实际上也只是“高级”代码指令!您永远不会真正知道它们是如何在位级别实现的,如果您正在阅读旧的 8086(单核)汇编器手册,它可能与您期望的不太一样。Windows 确实提供了与本机系统兼容的原子操作符,建议您使用这些操作符,而不是对原子操作做任何基本假设。

为什么使用 Windows 运算符?因为 Windows 所做的第一件事就是确定运行它的机器是什么。它确保正确的关键方面之一是存在哪些原子操作以及它们将如何工作。如果您希望您的代码在未来的任何处理器上运行良好,您可以在自己的代码中复制(并不断更新)所有这些工作,或者您可以利用 Windows 在启动时已经完成所有这些工作的事实。然后,它在运行时将必要的代码合并到其 API 中。

阅读有关原子操作的 MSDN 页面。Windows API 为您呈现这些。它们有时可能看起来笨重或笨拙 - 但它们是未来的证明,它们将始终按照锡上所说的那样工作。

我怎么知道这个?好吧,因为如果他们不这样做 - 那么您将无法运行 Windows。句号。没关系运行自己的代码。

每当您编写代码时,了解Parsimony并考虑Occam 剃刀总是一个好主意。换句话说,如果 Windows 已经这样做了,并且你的代码需要 Windows 来运行,那么就使用 Windows 已经在做的事情,而不是尝试许多替代的和越来越复杂的假设解决方案,这些解决方案可能有效也可能无效。做任何其他事情都只是在浪费你的时间(当然,除非你是这样做的)。

于 2016-03-11T14:18:14.190 回答
1

Indy 代码在 IdThreadSafe.pas 中包含一些原子/线程安全的数据类型:

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean
  • TIdThreadSafeString
  • TIdThreadSafeStringList 等等...
于 2009-03-21T18:49:26.680 回答