Leo*_*Leo 12 delphi multithreading thread-safety
我声明了两个全局变量:
var
gIsRunning: Boolean = False;
gLogCounter: Integer = 0;
Run Code Online (Sandbox Code Playgroud)
这些变量仅在主线程中写入,并在其他线程中读取.在这种情况下,这些变量是否安全?
klu*_*udg 49
你可能在谈论原子变量.整数和布尔变量都是原子的.布尔值(字节)总是原子的,整数(32位)是原子的,因为编译器正确地对齐它们.
原子性意味着任何读或写操作都作为一个整体执行.如果线程A同时执行原子写入和线程B原子读取相同数据,则线程B读取的数据始终是一致的 - 线程B读取的某些位不可能从当前写入操作获得并且上一次写入的一些位(由线程A)
但原子性并不意味着线程安全 - 您可以使用原子变量轻松编写不安全的代码.变量本身不能是线程安全的 - 只有代码作为一个整体可以(或不是)线程安全.
Mas*_*ler 13
只要只有一个线程可以写入它们,那么是的,它们是线程安全的.线程安全的真正问题是两个线程试图同时修改一个值,你不会在这里.
如果它们更大,比如记录或数组,则可能会遇到一个线程尝试写入值,中途获取,然后获取上下文切换以及另一个线程读取部分(因此损坏)数据的问题.但是对于单独的布尔值(1字节)和整数(4字节)值,编译器可以自动对齐它们,以便CPU可以保证对它们的所有读取和写入都是原子的,因此这不是问题.
Ken*_*ssa 11
简单类型是"线程安全的",只要它们可以从存储器中单次读取(或以单次写入方式)读取.我不确定它是由CPU内存总线宽度还是它们的"整数"大小(32位对64位cpu)定义的.也许其他人可以澄清那一部分.
我知道现在的读取大小至少是32位.(回到英特尔286天,它一次只有8位).
但是有一件事要知道.尽管它一次可以读取32位,但它无法在任何地址开始读取.它需要是32位(或4字节)的倍数.因此,如果它没有与32位对齐,即使整数也可以在2次后续读取中读取.值得庆幸的是,编译器会自动将所有字段与32位(甚至64位)对齐.
但是有一个例外,打包记录永远不会对齐,因此,即使是这样的记录中的整数也不是线程安全的.
由于它们的大小,int64也不是线程安全的.关于大多数浮动类型也可以这样说.(单身,我相信).
现在,考虑到所有这些,在某种情况下,您可以从多个线程实际编写一个全局变量,并且仍然是"线程安全的".
例如,
var
LastGoodValueTested : Integer
procedure TestValue(aiValue : Integer);
begin
if ValueGood(aiValue) then
LastGoodValue := aiValue
end;
Run Code Online (Sandbox Code Playgroud)
在这里,你可以从多个线程调用例程TestValue,你不会破坏 LastGoodValueTested变量.可能会发生写入变量的值不是最后一个.(如果在ValueGood(aiValue)和赋值之间发生线程上下文切换).因此,根据需要,它可能/可能不被接受.
现在,
var
gLogCounter: Integer = 0;
procedure Log(S : string);
begin
gLogCounter := gLogCounter + 1;
end;
Run Code Online (Sandbox Code Playgroud)
在这里,你实际上可以破坏计数器,因为它不是一元操作.你首先阅读变量.然后添加1.然后你把它保存回来.线程上下文切换可以在这些操作的中间发生.因此,这是需要同步的情况.
在这种情况下,它可以重写为
procedure Log(S : string);
begin
InterlockedIncrement(gLogCounter);
end;
Run Code Online (Sandbox Code Playgroud)
我认为这比使用关键部分要快一些......但我不确定.
不,他们不会是线程安全的,必须使用例如一个关键部分,使用访问这些变量InitializeCriticalSection,EnterCriticalSection和LeaveCriticalSection函数
//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);
Run Code Online (Sandbox Code Playgroud)