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

发布于 2024-07-12 09:20:10 字数 155 浏览 7 评论 0原文

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

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

Are 'boolean' variables thread-safe for reading and writing from any thread? I've seen some newsgroup references to say that they are. Are any other data types available? (Enumerated types, short ints perhaps?)

It would be nice to have a list of all data types that can be safely read from any thread and another list that can also be safely written to in any thread without having to resort to various synchronization methods.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

耶耶耶 2024-07-19 09:20:10

请注意,您可以使 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 调用。

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

Please note that you can make essentially everything in delphi unthreadsafe. While others mention alignment problems on boolean this in a way hides the real problem.

Yes, you can read a boolean in any thread and write to a boolean in any thread if it's correctly aligned. But reading from a boolean you change is not necessarily "thread safe" anyway. Say you have a boolean you set to true when you've updated a number so that another thread reads the number.

if NumberUpdated then
begin
  LocalNumber = TheNumber;
end;

Due to optimizations the processor makes TheNumber may be read before NumberUpdated is read, thus you may get the old value of TheNumber eventhough you updated NumberUpdated last.

Aka, your code may become:

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

Imho, a basic rule of thumb:
"Reads are thread safe. Writes are not thread safe."
So if you're going to do a write protect the data with synchronization everywhere you read the value while a write could potentially occur.
On the other hand, if you only read and write a value in one thread, then it's thread safe. So you can do a large chunk of writing in a temporary location, then synchronize an update of applicationwide data.

Bonus blurb:

The VCL is not thread safe. Keep all modification of ui stuff in the main thread. Keep the creation of all ui stuff in the main thread too.

Many functions are not thread safe either, while others are, it often depends on the underlying winapi calls.

I don't think a "list" would be helpful as "thread safe" can mean a lot of stuff.

机场等船 2024-07-19 09:20:10

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

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

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

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

This is not a question of data types being thread-safe, but it is a question of what you do with them. Without locking no operation is thread-safe that involves loading a value, then changing it, then writing it back: incrementing or decrementing a number, clearing or setting an element in a set - they are all not thread-safe.

There is a number of functions that allow for atomic operations: interlocked increment, interlocked decrement, and interlocked exchange. This is a common concept, nothing specific to Windows, x86 or Delphi. For Delphi you can use the InterlockedFoo() functions of the Windows API, there are several wrappers around those too. Or write your own. The functions operate on integers, so you can have atomic increment, decrement and exchange of integers (32 bit) with them.

You can also use assembler and prefix ops with the lock prefix.

For more information see also this StackOverflow question.

奢望 2024-07-19 09:20:10

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

On 32-bit architecture, only properly aligned 32-bit or less data types should be considered atomic. 32-bit values must be 4-aligned (address of the data must be evenly divisible by four). You probably wouldn't run into interleaving at such tight level, but theoretically you could have double, Int64 or Extended non-atomic write.

夜访吸血鬼 2024-07-19 09:20:10

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

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

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

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

每当您编写代码时,理解简约并考虑奥卡姆剃刀。 换句话说,如果 Windows 已经做到了这一点,并且您的代码需要 Windows 来运行,那么就使用 Windows 已经做到的事情,而不是尝试许多可能有效或可能无效的替代且日益复杂的假设解决方案。 做其他任何事情都只是浪费你的时间(当然除非那是你想做的)。

With multi-core RISC processing and separate core cache memory being in the mix of a modern processor, it is no-longer the case that any 'trivial' high-level language read or write construct (or for that matter many once-upon-a-time 8086 'atomic' assembly instructions) can be considered atomic. Indeed, unless an assembler instruction is specifically designed to be atomic, it probably is not atomic - and that includes most mechanisms for memory reads. Even a long integer read at assembler level can be corrupted by a simultaneous write from another processor core that is sharing the same memory and using asynchronous cache update actions at the RISC-processor level. Remember that on a processor comprising multiple RISC cores, even assembly language instructions are effectively just "higher-level" code instructions! You never really know just how they are being implemented at the bit level, and it might not be quite what you expected if you were reading an old 8086 (single-core) assembler manual. Windows does provide native-system compatible atomic operators, and you would be well advised to use these rather than make any base assumptions about atomic operations.

Why use the Windows operators? Because one of the first things that Windows does is establish what the machine is that it is running upon. One of the key aspects it ensures it gets right is what atomic operations there are and how they will work. If you want your code to work well into the future upon any future processor, you can either duplicate (and constantly update) all this effort in your own code, or you can make use of the fact that Windows did it all already at startup. It then incorporated the necessary code into its API at runtime.

Read the MSDN pages on atomic operations. The Windows API surfaces these for you. They may sometimes seem clunky or clumsy - but they are future proof and they will always work exactly as it says on the tin.

How do I know this? Well, because if they didn't - then you wouldn't be able to run Windows. Full-stop. Never mind running your own code.

Whenever you write code, it is always a good idea to understand Parsimony and consider Occam's razor. In other words, if Windows already does it, and your code needs Windows to run, then use what Windows already does, rather than trying out many alternative and increasingly complex hypothetical solutions that may or may not work. Doing anything else is just a waste of your time (unless of course that is what you are in to).

芯好空 2024-07-19 09:20:10

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

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean
  • TIdThreadSafeString
  • TIdThreadSafeStringList
    还有更多...

The Indy code contains some atomic / thread safe data types in IdThreadSafe.pas:

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean
  • TIdThreadSafeString
  • TIdThreadSafeStringList
    and some more ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文