I've got a singleton that can be called by multiple threads.
I do some lookup of data pretty often and I what to cache the data so that I don't have to repeat the same lookup again and again.
I'd like to do something akin to using static local variables, but in a thread-safe way. I suspect the code below is watertight. Is this correct?
type
TPrevious = record
public
Fontname: string;
FontSize: integer;
Canvas: pointer;
Width: integer;
end;
threadvar Previous: TPrevious;
function TEditorOptions.GetEditorFontWidth(const Canvas: TCanvas): integer;
var
Font: TFont;
//var //static vars <<-- static var != threadsafe
// PreviousFontName: string = '';
// PreviousFontSize: integer = 0;
// PreviousCanvas: pointer = nil;
// PreviousWidth: integer = 0;
begin
{1: I'm assuming a managed threadvar is always initialized to Default(T)}
if (Previous.Fontname <> '') then begin
//Cache the values, so we don't recalculate all the time.
//Caching is per thread, but that's fine.
if (SameText(Previous.FontName, FFontName)) and (Previous.FontSize = FFontSize)
and (pointer(Canvas) = Previous.Canvas) then Exit(Previous.Width);
end;
Previous.Canvas := pointer(Canvas);
Previous.FontName := FFontName;
Previous.FontSize := FFontSize;
Result:= SomeCalculation(Canvas, FFontName, FFontSize);
....
Previous.Width:= Result;
....
end;
I have 2 questions:
A: Am I correct in assuming that managed threadvars like the string FontName
are always initialized to Default(T)
(i.e. ''
)
B: Is this code fully threadsafe/ re-entrant?
Thread safety is a computer programming concept applicable to multi-threaded code. Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction.
Thread SafetyStatic variables are not thread safe. Instance variables do not require thread synchronization unless shared among threads. But, static variables are always shared by all the threads in the process. Hence, access to static variable is not thread safe.
Given the structure of the JVM, local variables, method parameters, and return values are inherently "thread-safe." But instance variables and class variables will only be thread-safe if you design your class appropriately. As an example of a class that is not thread-safe, consider the RGBColor class, shown below.
On its stack(basically thread stack), local primitives and local reference variables are stored. Hence one thread does not share its local variables with any other thread as these local variables and references are inside the thread's private stack. Hence local variables are always thread-safe.
Any threadvar
instance is filled with zero, so your string
variable is properly initialized.
Sadly, threadvar
do not handle the memory of their managed type...
As a result, you need to release each string
inside your Previous
variable.
In practice, I do not store managed types in threadvar, but use another pattern (like injection at constructor level).
Small performance hit: access to each Previous.xxxxx
member has a performance cost: you may rather fill a local variable pointer with @Previous
, then use this pointer to access the fields (or use with Previous do
- but this syntax may be confusing).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With