具有“线程安全”读/写操作的Delphi数据类型列表?

时间:2009-02-04 04:09:52

标签: delphi thread-safety atomic

'boolean'变量是否可以线程安全地从任何线程读取和写入?我已经看到一些新闻组的引用说它们是。是否有其他数据类型? (列举的类型,也许是短的内容?)

拥有一个可以安全地从任何线程和另一个列表中读取的所有数据类型的列表会很高兴,这些列表也可以安全地写入任何线程,而不必采用各种同步方法。

5 个答案:

答案 0 :(得分:8)

请注意,你可以在delphi unthreadsafe中创建基本上所有内容。而其他人则提到布尔值上的对齐问题,这在某种程度上隐藏了真正的问题。

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

if NumberUpdated then
begin
  LocalNumber = TheNumber;
end;

由于优化,处理器使得可以在读取NumberUpdated之前读取TheNumber,因此,尽管您最后更新了NumberUpdated,但您可能会获得TheNumber的旧值。

Aka,您的代码可能会变成:

temp = TheNumber;
if NumberUpdated the
begin
  LocalNumber = temp;
end;
Imho,一个基本的经验法则:
“读取是线程安全的。写入不是线程安全的。”
因此,如果您要进行写保护,同步无处不在,您可以在写入可能时读取该值。
另一方面,如果您只在一个线程中读取和写入值,那么它的线程安全。因此,您可以在临时位置执行大量写入操作,然后同步应用程序范围数据的更新。

Bonus blurb:

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

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

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

答案 1 :(得分:7)

这不是数据类型是线程安全的问题,但它是你用它们做什么的问题。没有锁定没有操作是线程安全的,涉及加载一个值,然后更改它,然后将其写回:递增或递减一个数字,清除或设置一个集合中的元素 - 它们都不是线程安全的。

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

您还可以使用汇编程序和前缀ops和锁前缀。

有关详细信息,请参阅this StackOverflow问题。

答案 2 :(得分:5)

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

答案 3 :(得分:2)

随着多核RISC处理和独立的核心高速缓冲存储器与现代处理器的混合,不再是任何“琐事”的情况。高级语言读或写构造(或许多次一次8086和原子组装指令)可以被认为是原子的。实际上,除非汇编指令是专门设计为原子的,否则它可能不是原子的 - 并且包括大多数内存读取机制。即使是汇编程序级别的长整数读取也可能被来自另一个处理器核心的同时写入损坏,该处理器核心共享相同的内存并在RISC处理器级别使用异步缓存更新操作。请记住,在包含多个RISC内核的处理器上,即使汇编语言指令实际上只是“更高级别”。代码说明!你真的不知道它们是如何在比特级实现的,如果你正在阅读旧的8086(单核)汇编程序手册,它可能不是你所期望的。 Windows确实提供了与本机系统兼容的原子操作符,建议您使用这些操作符,而不是对原子操作做出任何基本假设。

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

阅读有关原子操作的MSDN页面。 Windows API为您提供了这些表面。它们有时看起来很笨拙或笨拙 - 但它们是未来的证据,它们将始终像锡上所说的完全一样。

我怎么知道这个?好吧,因为如果他们没有 - 那么你将无法运行Windows。句号。别介意运行自己的代码。

每当您编写代码时,最好先了解Parsimony并考虑Occam's razor。换句话说,如果Windows已经执行了,并且您的代码需要运行Windows,那么请使用Windows已经执行的操作,而不是尝试许多替代且日益复杂的假设解决方案,这些解决方案可能有效,也可能无效。做其他事只是浪费你的时间(除非你当然是这样)。

答案 4 :(得分:1)

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

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean
  • TIdThreadSafeString
  • TIdThreadSafeStringList 还有一些......