简单类型是“线程安全的”,只要它们可以从内存中一次读取(或一次写入)即可。我不确定它是由 CPU 内存总线宽度还是它们的“整数”大小(32 位与 64 位 cpu)定义的。也许其他人可以澄清这部分。
我知道现在的读取大小至少是 32 位。 (在 Intel 286 年代,一次只有 8 位)。
不过,有 1 件事需要了解。尽管它一次可以读取 32 位,但它不能从任何地址开始读取。它需要是 32 位(或 4 个字节)的倍数。因此,如果整数未与 32 位对齐,则即使是整数也可以在 2 次后续读取中读取。值得庆幸的是,编译器会自动将几乎所有字段对齐到 32 位(甚至 64 位)。
但有一个例外,打包的记录永远不会对齐,因此,即使这样的记录中的整数也不是线程安全的。
由于它们的大小,int64 也不是线程安全的。大多数浮动类型也是如此。 (我相信除了单身)。
现在,考虑到所有这些,在某些情况下,您实际上可以从多个线程编写一个全局变量,并且仍然是“线程安全的”。
例如,
var
LastGoodValueTested : Integer
procedure TestValue(aiValue : Integer);
begin
if ValueGood(aiValue) then
LastGoodValue := aiValue
end;
在这里,您可以从多个线程调用例程 TestValue,并且不会损坏 LastGoodValueTested 变量。不过,写入变量的值可能不是最后一个。 (如果在 ValueGood(aiValue) 和分配之间发生线程上下文切换)。因此,根据需要,它可能会/可能不会被接受。
现在,
var
gLogCounter: Integer = 0;
procedure Log(S : string);
begin
gLogCounter := gLogCounter + 1;
end;
在这里,您实际上可以损坏计数器,因为它不是一元操作。您首先读取变量。然后加1。然后你把它存回去。线程上下文切换可能发生在这些操作的中间。所以这是需要同步的情况。
在这种情况下,它可以重写为
procedure Log(S : string);
begin
InterlockedIncrement(gLogCounter);
end;
我认为这比使用关键部分要快一些……但我不确定。