我宣布了两个全局变量:
var
gIsRunning: Boolean = False;
gLogCounter: Integer = 0;
这些变量只写在主线程中,并在其他线程中读取。在这种情况下,这些变量是否安全?
答案 0 :(得分:49)
你可能在谈论原子变量。整数和布尔变量都是原子的。布尔值(字节)总是原子的,整数(32位)是原子的,因为编译器正确地对齐它们。
原子性意味着任何读或写操作都是作为一个整体执行的。如果线程A同时执行原子写入和线程B原子读取相同数据,则线程B读取的数据始终是一致的 - 线程B读取的某些位不可能从当前写入操作获得并且前一次写入的一些位(通过线程A)
但原子性并不意味着线程安全 - 您可以使用原子变量轻松编写不安全的代码。变量本身不能是线程安全的 - 只有代码作为一个整体可以(或不是)线程安全。
答案 1 :(得分:13)
只要只有一个线程可以写入它们,那么是的,它们是线程安全的。线程安全的真正问题是两个线程试图同时修改一个值,你不会在这里。
如果它们更大,如记录或数组,则可能会遇到一个线程尝试写入值,中途获取,然后获取上下文切换以及另一个线程读取部分(因此损坏)数据的问题。但是对于单独的布尔值(1字节)和整数(4字节)值,编译器可以自动对齐它们,以便CPU可以保证对它们的所有读取和写入都是原子的,因此这不是问题。
答案 2 :(得分:11)
简单类型是“线程安全的”,只要它们可以在内存中以单次读取(或单次写入)读取即可。我不确定它是由CPU内存总线宽度还是它们的“整数”大小(32位对64位cpu)定义的。也许其他人可以澄清这一部分。
我知道现在的读取大小至少为32位。 (回到英特尔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;
我认为这比使用关键部分要快一些......但我不确定。
答案 3 :(得分:8)
不是它们不是线程安全的,您必须使用InitializeCriticalSection
,EnterCriticalSection
和LeaveCriticalSection
函数来使用例如关键部分来访问此类变量/ p>
//declaration of your global variables
var
MyCriticalSection: TRTLCriticalSection;
gIsRunning: Boolean;
gLogCounter: Integer;
//before the threads starts
InitializeCriticalSection(MyCriticalSection);
//Now in your thread
EnterCriticalSection(MyCriticalSection);
//Here you can make changes to your variables.
gIsRunning:=True;
inc(gLogCounter);
//End of protected block
LeaveCriticalSection(MyCriticalSection);