Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Delphi simple types thread safe?

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?

like image 731
Leo Avatar asked Mar 30 '11 01:03

Leo


People also ask

How do I know if a method is thread safe?

To test if the combination of two methods, a and b, is thread-safe, call them from two different threads. Put the complete test in a while loop iterating over all thread interleavings with the help from the class AllInterleavings from vmlens. Test if the result is either an after b or b after a.

Why are global variables not thread safe?

Global variables are still not thread safe because there's still no protection against most race conditions. You can still have a scenario where one worker gets a value, yields, another modifies it, yields, then the first worker also modifies it.

How do I use thread in Delphi?

To use a thread object in your application (and to create a descendant of Classes. TThread): Choose one: File > New > Other > Delphi Projects > Delphi Files > Thread Object.

Are atomic variables thread safe?

atomic which offers lock-free and thread-safe classes. Variables of these class types are called as atomic variables. There are 12 classes within this package.


1 Answers

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.

like image 100
Ken Bourassa Avatar answered Sep 17 '22 17:09

Ken Bourassa