Reputation: 1957
I declared two global variables:
var
gIsRunning: Boolean = False;
gLogCounter: Integer = 0;
These variables are written only in the main thread, and read in other threads. In this case, are these variables thread safe?
Upvotes: 13
Views: 9289
Reputation: 49
For newer Delphi versions the unit SynObjs contains helper classes:
//declaration of your global variables
var
MyCriticalSection: TCriticalSection;
gIsRunning: Boolean;
gLogCounter: Integer;
//before the threads starts
MyCriticalSection := TCriticalSection.Create;
//Now in your thread
if MyCriticalSection.TryEnter then
begin
//Here you can make changes to your variables.
gIsRunning:=True;
inc(gLogCounter);
//End of protected block
MyCriticalSection.Leave;
end;
Upvotes: 0
Reputation: 136401
No they are not are thread safe, you must access such variables using for example a critical section, using InitializeCriticalSection
, EnterCriticalSection
and LeaveCriticalSection
functions
//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);
Upvotes: 8
Reputation: 6467
Simple types are "thread-safe" as long as they can be read in a single read (or written in a single write) from the memory. I'm not sure if it's defined by the CPU memory bus width, or their "integer" size (32 bits vs 64 bits cpu). Maybe someone else can clarify that part.
I know the read size nowaday is at least 32 bits. (Back in the Intel 286 days, it was only 8 bits at a time).
There is 1 thing to know about this though. Even though it can read 32 bits at a time, it cannot start a read at just any address. It needs to be a multiple of 32 bits (or 4 bytes). So, even an integer could be read in 2 subsequent reads if it's not aligned to 32 bits. Thankfully, the compiler will align pretty much all fields to 32 bits (or even 64 bits) automatically.
There is an exception to this though, packed records are never aligned, and thus, even an integer in such a record wouldn't be thread safe.
Because of their size, int64 are not thread safe either. The same can be told about most floating types. (Except Single I believe).
Now, with all that in mind, there is some situation where you could actually write a global variable from multiple thread and still be "thread-safe".
For example,
var
LastGoodValueTested : Integer
procedure TestValue(aiValue : Integer);
begin
if ValueGood(aiValue) then
LastGoodValue := aiValue
end;
here, you could call the routine TestValue from multiple threads and you wouldn't corrupt the LastGoodValueTested variables. It could happen that value that is written to the variable wouldn't be the very, very last though. (If a thread context switch happen between ValueGood(aiValue) and the assignation). So, depending on the needs, it may/may not be acceptable.
Now,
var
gLogCounter: Integer = 0;
procedure Log(S : string);
begin
gLogCounter := gLogCounter + 1;
end;
Here, you can actually corrupt the counter because it's not a unary operation. You first read the variable. Then add 1 to it. Then you save it back. A thread context switch can happen in the middle of those operation. So that is a case that requires synchronization.
In that case, it could be rewritten to
procedure Log(S : string);
begin
InterlockedIncrement(gLogCounter);
end;
I would think this is slightly faster than using critical sections... But I'm not sure.
Upvotes: 13
Reputation: 27493
You are probably saying about atomic variables. Integer and Boolean variables are atomic. Booleans (bytes) are always atomic, integers (32-bits) are atomic because the compiler properly aligns them.
Atomicity means that any read or write operation is executed as a whole. If a thread A executes atomic write and a thread B atomic read of the same data at the same time, the data read by thread B is always consistent - it is impossible that some bits read by thread B are obtained from the current write operation and some bits from the previous write (by thread A)
But atomicity does not mean thread safety - you can easily write unsafe code with atomic variables. A variable itself cannot be threadsafe - only a code as a whole can be (or not) threadsafe.
Upvotes: 50
Reputation: 84550
As long as there is only one thread that can write to them, then yes, they're thread safe. The real problem with thread safety is two threads trying to modify a value at the same time, and you won't have that here.
If they were larger, like records or arrays, you might have issues with one thread trying to write a value, getting partway through, then getting context-switched and another thread reads partial (and therefore corrupt) data. But for individual boolean (1 byte) and integer (4 byte) values, the compiler can automatically align them in such a way that the CPU can guarantee that all reads and writes to them are atomic, so that isn't a problem here.
Upvotes: 15