ParseInput overload 1 (all parameters explicit) is fully
thread-safe. Each call operates entirely on its own stack and local
variables; no shared state is read or written. It can be called
concurrently from any number of threads without synchronization.
// Safe to call from multiple threads simultaneously.
R := ParseInput(Input, itDate, '~', [], sopEqual, sqlMySQL);Overloads 2–5 read the global variable GDefaultDialect
at call time. They are safe to use from multiple threads provided
SetSQLDialect is never called after multi-threaded
processing has begun. The typical safe pattern is to call
SetSQLDialect once during application startup — before any
secondary threads are created — and then use overloads 2–5 freely from
any thread:
// Main thread, application startup.
SetSQLDialect(sqlElevateDB);
// Worker threads: safe, because SetSQLDialect is never called again.
R := ParseInput(Input, itDate);If the dialect must change at runtime while threads are running, use overload 1 exclusively and pass the dialect explicitly on each call.
Concurrent Parse() calls on different
TEgStrParser instances are thread-safe. Each instance
stores its own FSqlDialect and passes it directly to
ParseInput overload 1. No global variable is read or
written.
// Safe: Parser1 and Parser2 are distinct instances.
// Thread A:
R := Parser1.Parse(Input1, itDate);
// Thread B (simultaneously):
R := Parser2.Parse(Input2, itInteger);Concurrent calls on the same
TEgStrParser instance are not thread-safe.
FLastResult and FLastControl are written on
every call without synchronization; concurrent writes produce a data
race.
If a single component must be shared across threads, protect each call with a lock:
// One lock per shared component instance.
FCritSect.Enter;
try
R := Parser1.Parse(Input, itDate);
finally
FCritSect.Leave;
end;Alternatively, create a separate TEgStrParser instance
per thread, or use ParseInput overload 1 directly in worker
threads and reserve the component for the main thread.
SetSQLDialect and GetSQLDialect access the
global variable GDefaultDialect without synchronization. Do
not call either function from a thread while another thread may be
calling ParseInput overloads 2–5 or the other dialect
accessor.
// Unsafe: dialect may change while worker thread reads it.
// Worker thread:
R := ParseInput(Input, itDate); // reads GDefaultDialect
// Main thread (simultaneously):
SetSQLDialect(sqlMySQL); // writes GDefaultDialectParseInput reads FormatSettings (the global
Delphi locale settings) to initialize the per-call SqlFmt
and LocaleFmt records. In Delphi 11 and later,
FormatSettings is a thread-local variable by default when
{$DEFINE POSIX} or the RTL is configured for thread safety.
On Windows with the default RTL settings, FormatSettings is
a global variable; changing it from one thread while another is calling
ParseInput is a data race.
In practice, FormatSettings is normally set once at
startup and not changed thereafter, which is safe. If per-thread locale
overrides are needed, use the TFormatSettings-based
overloads of the RTL functions directly rather than modifying the
global.
| Scenario | Safe? |
|---|---|
ParseInput overload 1, multiple threads |
Yes |
ParseInput overloads 2–5, multiple threads,
SetSQLDialect called only at startup |
Yes |
ParseInput overloads 2–5, SetSQLDialect
called at runtime from another thread |
No |
TEgStrParser.Parse(), different instances, multiple
threads |
Yes |
TEgStrParser.Parse(), same instance, multiple
threads |
No |
SetSQLDialect / GetSQLDialect concurrent
with overloads 2–5 |
No |