I have an object x that needs to be accessed from several (5+ threads). The structure of the object is
Tx = class
private
Fx: integer;
public
property x: integer read Fx read Fx;
etc;
What is the better (most elegant) way of protection:
a)
Tx = class
private
Fx: integer;
public
property x: integer read Fx read Fx;
public
constructor Create; <--- Create the criticalsection here
destructor Destroy; <--destroy it here
etc;
var
cs: TCriticalSection;
Obj: Tx;
function GetSafeObject(): Tx;
begin
CS.Enter;
try
Result:= Obj;
finally
CS.Leave;
end;
and access always the object as GetSafeObj().x:= 3;
or
Tx = class
private
Fx: integer;
FCS: TCriticalSection;
public
property x: integer read GerX read SetX;
public
constructor Create; <--- Create the criticalsection here
destructor Destroy; <--destroy it here
etc;
where
function Tx.Getx(): integer;
begin
CS.Enter;
try
Result:= Fx;
finally
CS.Leave;
end;
end;
end;
and always access the object normally. I guess the first option is more elegant, even if both methods should work fine. Ay comments?
Go for option B, making the critical section internal to the object. If the user of the class has to make use of an external function to safely access the instance, it is inevitable that somebody won't and the house will come tumbling down.
You also need to think about what operational semantic you are wanting to protect from multiple concurrent reads & writes. If you put a lock inside your getter and setter, you can guarantee that your object is internally coherent, but users of your object may see multithreading artifacts. For example, if thread A writes 10 to a property of your object, and thread B writes 50 to that property of the same object, only one of them can be last one in. If A happens to go first, then A will observe that they wrote a 10 to the property, but when they read it back again they see B's 50 that snuck in there in the gap between A's read-after-write.
Note also that you don't really need a lock to protect a single integer field. Aligned pointer sized integer writes are atomic operations on just about every hardware system today. You definitely need a lock to protect multi-piece data like structs or multi-step operations like changing two related fields at the same time.
If there is any way you can rework your design to make these objects local to a particular operation on a thread, do it. Making local copies of data may increase your memory footprint slightly, but it can dramatically simplify your code for multithreading and run faster than leaving mutex landmines all over the app. Look for other simplifying assumptions as well - if you can set up your system so that the object is immutable while its available to multiple threads, then the object doesn't need any lock protections at all. Read-only data is good for sharing across threads. Very very good.
Making the CS be a member of the object and using the CS inside of property getter/setter methods is the correct approach. The other approach does not work because it locks and unlocks the CS before the object is actually acessed, so the property value is not protected at all.
An easy way is to have a thread-safe wrapper around the object, similar to TThreadList. The wrapper needs two methods: Lock (to enter the critical section and return the inner object) and Unlock (to leave the critical section).
Related
Is it ok to read data from TStringList without any form of synchronization? For example synchronization with main thread.
Example code
var MyStringList:TStringList; //declared globally
procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer);
var x:integer;
begin
for x:=0 to MaxInt do MyStringList.Add(FloatToStr(Random));
end;
procedure TForm1.ButtonClick(Sender: TObject);
var x:integer;
SumOfRandomNumbers:double;
begin
for x:=0 to MyStringList.Count-1 do
SumOfRandomNumbers:=SumOfRandomNumbers+StrToFloat(MyStringList.Strings[x]);
end;
or Should I protect access to MyStringList with EnterCiticalSection
var MyStringList:TStringList; //declared globally
procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer);
var x:integer;
begin
for x:=0 to MaxInt do
begin
EnterCriticalSection(MySemaphore);
MyStringList.Add(FloatToStr(Random));
LeaveCriticalSection(MySemaphore);
end;
end;
procedure TForm1.ButtonClick(Sender: TObject);
var x:integer;
SumOfRandomNumbers:double;
begin
for x:=0 to MyStringList.Count-1 do
begin
EnterCriticalSection(MySemaphore);
SumOfRandomNumbers:=SumOfRandomNumbers+StrToFloat(MyStringList.Strings[x]);
LeaveCriticalSection(MySemaphore);
end;
end;
First, no TStringList is not thread-safe.
Second, attempting to make it so would be a terrible idea for a low-level container that in the vast majority of cases would not be shared across multiple threads.
Third, the naive code you propose to make it thread-safe is woefully insufficient. It falls well short of making it truly thread-safe - which is part of the problem in trying to do so generically.
In the text of your question you ask:
Is it ok to read data from TStringList without any form of synchronisation?
Yes it is okay. In fact, that is preferred because it is more efficient.
However, if the data is shared across threads, you may run into problems. Which is why you should minimise the amount of data (not just string lists) shared across threads. And if you do need to share data, do so in a suitably controlled fashion.
Expanding on point 3
The reason your code is not thread-safe is that it falls short of protecting all your data from shared access. This is a common misunderstanding in multi-threaded development: "I just need to wrap certain operations with locks and all will be fine."
The point is, if your list is shared, you are:
Sharing the structures that represent the container.
AND you are sharing the data members (the actual strings) themselves.
When dealing with strings, this goes a step further, because the way Delphi manages strings means they could be shared (through internal reference counting) with other strings of the same value in an entirely different area of the application.
While it is possible your proposed locking strategy might be suitable for your current requirements, it is far from being generally thread-safe.
Conclusion
If you want to write thread-safe code the onus is on you to:
Understand the data access paths.
Minimise sharing between threads (by far the best bang for buck).
And to implement the best strategy to share the data safely (of which there are many options, and locking is not guaranteed to be best in any case).
Sidenote
I indicated earlier that your locking technique only "might be suitable for your current requirements" because I do not believe you have really given an indication as to you real requirements. If you have then you really do need to take note of the following:
In the code you have presented there would be absolutely no benefit in making your TStringList "thread-safe". You populate the list in a loop, and you read values in a second loop. You're doing absolutely nothing to use the data concurrently.
The closest your code should come to multi-threading is: It would be a good idea to process both loops off the main thread to avoid blocking the UI. In which case, the background thread should NOT share its TStringList instance. And can simply synchronise with the main thread to report the result (and possibly progress updates).
By not sharing data that doesn't need to be shared, you can bypass the need for locks entirely. They would be an unnecessary overhead. And you can be happy that TStringList doesn't have a built-in "thread-safety" mechanism.
No, it isn't. There is no mechanism inside of TStringList, that locks for example .Add() or .GetStrings().
Unfortunately there is nothing built in like TThreadList, that is a threadsafe wrapper for TList. But you could build that easily on your own.
Here is a simple example for a synchronized decorator of TStringList, in that I cover the case for Add():
TThreadStringList = class
private
FStringList: TStringList;
FCriticalSection: TRtlCriticalSection;
// ...
public
function Add(const S: string): Integer;
// ...
end;
// ...
TThreadStringList.Add(const S: string): Integer;
begin
EnterCriticalSection(FCriticalSection);
try
Result:= Add(S);
finally
LeaveCriticalSection(FCriticalSection);
end;
end;
It should be easy, to apply this to all other methods you need.
Bear in mind, that you have to initialize the critical section, before you can use it, and to delete it afterwards.
I have a thread class TValidateInvoiceThread:
type
TValidateInvoiceThread = class(TThread)
private
FData: TValidationData;
FInvoice: TInvoice; // Do NOT free
FPreProcessing: Boolean;
procedure ValidateInvoice;
protected
procedure Execute; override;
public
constructor Create(const objData: TValidationData; const bPreProcessing: Boolean);
destructor Destroy; override;
end;
constructor TValidateInvoiceThread.Create(const objData: TValidationData;
const bPreProcessing: Boolean);
var
objValidatorCache: TValidationCache;
begin
inherited Create(False);
FData := objData;
objValidatorCache := FData.Caches.Items['TInvAccountValidator'];
end;
destructor TValidateInvoiceThread.Destroy;
begin
FreeAndNil(FData);
inherited;
end;
procedure TValidateInvoiceThread.Execute;
begin
inherited;
ValidateInvoice;
end;
procedure TValidateInvoiceThread.ValidateInvoice;
var
objValidatorCache: TValidationCache;
begin
objValidatorCache := FData.Caches.Items['TInvAccountValidator'];
end;
I create this thread in another class
procedure TInvValidators.ValidateInvoiceUsingThread(
const nThreadIndex: Integer;
const objValidatorCaches: TObjectDictionary<String, TValidationCache>;
const nInvoiceIndex: Integer; const bUseThread, bPreProcessing: Boolean);
begin
objValidationData := TValidationData.Create(FConnection, FAllInvoices, FAllInvoices[nInvoiceIndex], bUseThread);
objValidationData.Caches := objValidatorCaches;
objThread := TValidateInvoiceThread.Create(objValidationData, bPreProcessing);
FThreadArray[nThreadIndex] := objThread;
FHandleArray[nThreadIndex]:= FThreadArray[nThreadIndex].Handle;
end;
Then I execute it
rWait:= WaitForMultipleObjects(FThreadsRunning, #FHandleArray, True, 100);
Note I have removed some code out of here to try to keep it a bit simpler to follow
The problem is that my Dictionary is becoming corrupt
If I put a breakpoint in the constructor all is fine
However, in the first line of the Execute method, the dictionary is now corrupt.
The dictionary itself is a global variable to the class
Do I need to do anything special to allow me to use Dictionaries inside a thread?
I have also had the same problem with a String List
Edit - additional information as requested
TInvValidators contains my dictionary
TInvValidators = class(TSTCListBase)
private
FThreadArray : Array[1..nMaxThreads] of TValidateInvoiceThread;
FHandleArray : Array[1..nMaxThreads] of THandle;
FThreadsRunning: Integer; // total number of supposedly running threads
FValidationList: TObjectDictionary<String, TObject>;
end;
procedure TInvValidators.Validate(
const Phase: TValidationPhase;
const objInvoices: TInvoices;
const ReValidate: TRevalidateInvoices;
const IDs: TList<Integer>;
const objConnection: TSTCConnection;
const ValidatorCount: Integer);
var
InvoiceIndex: Integer;
i : Integer;
rWait : Cardinal;
Flags: DWORD; // dummy variable used in a call to find out if a thread handle is valid
nThreadIndex: Integer;
procedure ValidateInvoiceRange(const nStartInvoiceID, nEndInvoiceID: Integer);
var
InvoiceIndex: Integer;
I: Integer;
begin
nThreadIndex := 1;
for InvoiceIndex := nStartInvoiceID - 1 to nEndInvoiceID - 1 do
begin
if InvoiceIndex >= objInvoices.Count then
Break;
objInvoice := objInvoices[InvoiceIndex];
ValidateInvoiceUsingThread(nThreadIndex, FValidatorCaches, InvoiceIndex, bUseThread, False);
Inc(nThreadIndex);
if nThreadIndex > nMaxThreads then
Break;
end;
FThreadsRunning := nMaxThreads;
repeat
rWait:= WaitForMultipleObjects(FThreadsRunning, #FHandleArray, True, 100);
case rWait of
// one of the threads satisfied the wait, remove its handle
WAIT_OBJECT_0..WAIT_OBJECT_0 + nMaxThreads - 1: RemoveHandle(rWait + 1);
// at least one handle has become invalid outside the wait call,
// or more than one thread finished during the previous wait,
// find and remove them
WAIT_FAILED:
begin
if GetLastError = ERROR_INVALID_HANDLE then
begin
for i := FThreadsRunning downto 1 do
if not GetHandleInformation(FHandleArray[i], Flags) then // is handle valid?
RemoveHandle(i);
end
else
// the wait failed because of something other than an invalid handle
RaiseLastOSError;
end;
// all remaining threads continue running, process messages and loop.
// don't process messages if the wait returned WAIT_FAILED since we didn't wait at all
// likewise WAIT_OBJECT_... may return soon
WAIT_TIMEOUT: Application.ProcessMessages;
end;
until FThreadsRunning = 0; // no more valid thread handles, we're done
end;
begin
try
FValidatorCaches := TObjectDictionary<String, TValidationCache>.Create([doOwnsValues]);
for nValidatorIndex := 0 to Count - 1 do
begin
objValidator := Items[nValidatorIndex];
objCache := TValidationCache.Create(objInvoices);
FValidatorCaches.Add(objValidator.ClassName, objCache);
objValidator.PrepareCache(objCache, FConnection, objInvoices[0].UtilityType);
end;
nStart := 1;
nEnd := nMaxThreads;
while nStart <= objInvoices.Count do
begin
ValidateInvoiceRange(nStart, nEnd);
Inc(nStart, nMaxThreads);
Inc(nEnd, nMaxThreads);
end;
finally
FreeAndNil(FMeterDetailCache);
end;
end;
If I remove the repeat until and leave just WaitForMultipleObjects I still get lots of errors
You can see here that I am processing the invoices in chunks of no more than nMaxThreads (10)
When I reinstated the repeat until loop it worked on my VM but then access violated on my host machine (which has more memory available)
Paul
Before I offer guidance on how to resolve your problem, I'm going to give you a very important tip.
First ensure your code works single-threaded, before trying to get a multi-threaded implementation working. The point is that multi-threaded code adds a whole new layer of complexity. Until your code works correctly in a single thread, it has no chance of doing so in multiple threads. And the extra layer of complexity makes it extremely difficult to fix.
You might believe you've got a working single-threaded solution, but I'm seeing errors in your code that imply you still have a lot of resource management bugs. Here's one example with relevant lines only, and comments to explain the mistakes:
begin
try //try/finally is used for resource protection, in order to protect a
//resource correctly, it should be allocated **before** the try.
FValidatorCaches := TObjectDictionary<String, TValidationCache>.Create([doOwnsValues]);
finally
//However, in the finally you're destroying something completely
//different. In fact, there are no other references to FMeterDetailCache
//anywhere else in the code you've shown. This strongly implies an
//error in your resource protection.
FreeAndNil(FMeterDetailCache);
end;
end;
Reasons for not being able to use the dictionary
You say that: "in the first line of the Execute method, the dictionary is now corrupt".
For a start, I'm fairly certain that your dictionary isn't really "corrupt". The word "corrupt" implies that it's there, but its internal data is invalid resulting in inconsistent behaviour. It's far more likely that by the time the Execute method wants to use the dictionary, it has already been destroyed. So your thread is basically pointing to an area of memory that used to have a dictionary, but it's no longer there at all. (I.e. not "corrupt")
SIDE NOTE It is possible for your dictionary to truly become corrupt because you have multiple threads sharing the same dictionary. If different threads cause any internal changes to the dictionary at the same time, it could very easily become corrupt. But, assuming your threads are all treating the dictionary as read-only, you would need a memory overwrite to corrupt it.
So let's focus on what might cause your dictionary to be destroyed before the thread gets to use it. NOTE I can't see anything in the code provided, but there are 2 likely possibilities:
Your main thread destroys the dictionary before the child thread gets to use it.
One of your child threads destroys the dictionary as soon as it is destroyed resulting in all other threads being unable to use it.
In the first case, this would happen as follows:
Main Thread: ......C......D........
Child Thread ---------S......
. = code being executed
C = child thread created
- = child thread exists, but isn't doing anything yet
S = OS has started the child thread
D = main thread destroys dictionary
The point of the above is that it's easy to forget that the main thread can reach a point where it decides to destroy the dictionary even before the child thread starts running.
As for the second possibility, this depends on what is happening inside the destructor of TValidationData. Since you haven't shown that code, only you know the answer to that.
Debugging to pinpoint the problem
Assuming the dictionary is being destroyed too soon, a little debugging can quickly pinpoint where/why the dictionary is being destroyed. From your question, it seems you've already done some debugging, so I'm assuming you'll have no trouble following these steps:
Put a breakpoint on the first line of the dictionary's destructor.
Run your code.
If you reach Execute before reaching the dictionary's destructor, then the thread should still be able to use the dictionary.
If you reach the dictionary's destructor before reaching Execute, then you simply need to examine the sequence of calls leading to the object's destruction.
Debugging in case of a memory overwrite
Keeping an open mind about the possibility of a memory overwrite... This is a little trickier to debug. But provided you can consistently reproduce the problem it should be possible to debug.
Put a breakpoint in the thread's destructor.
Run the app
When you reach the above breakpoint, find the address of the of the dictionary by pressing Ctrl + F7 and evaluating #FData.Caches.
Now add a Data Breakpoint (use the drop-down from the Breakpoints window) for the address and the size of the dictionary.
Continue running, the app will pause when the the data changes.
Again, examine the call-stack to determine the cause.
Wrapping up
You have a number of questions and statements that imply misunderstandings about sharing data (dictionary/string list) between threads. I'll try cover those here.
There is nothing special required to use a Dictionary/StringList in a thread. It's basically the same as passing it to any other object. Just make sure the Dictionary/StringList isn't destroyed prematurely.
That said, whenever you share data, you need to be aware of the possibility of "race conditions". I.e. one thread attempts to access the shared data at the same time another thread is busy modifying it. If no threads are modifying the data, then there's no need for concern. But as soon as any thread is able to modify the data, the access needs to be made "thread-safe". (There are a number of ways to do this, please search for existing questions on SO.)
You mention: "The dictionary itself is a global variable to the class". Your terminology is not correct. A global variable is something declared at the unit level and is accessible anywhere. It's enough to simply say the dictionary is a member of or field of the class. When dealing with "globals", there are significantly different things to worry about; so best to avoid any confusion.
You may want to rethink how you initialise your threads. There are a few reasons you some entries of FHandleArray won't be initialised. Are you ok with this?
You mention AV on a machine that has more memory available. NOTE: Amount of memory is not relevant. And if you run in 32-bit mode you wouldn't have access to more than 4 GB in any case.
Finally, to make a special mention:
Using multiple threads to perform dictionary lookups is extremely inefficient. A dictionary lookup is an O(1) operation. The overhead of threading will almost certainly slow you down unless you intend doing a significant amount of processing in addition to the dictionary lookup.
PS - (not so big) mistake
I noticed the following in your code, and it's a mistake.
procedure TValidateInvoiceThread.Execute;
begin
inherited;
ValidateInvoice;
end;
The TThread.Execute method is abstract, meaning there's no implementation. Attempting to call an abstract method will trigger an EAbstractError. Luckily as LU RD points out, the compiler is able to protect you by not compiling the line in. Even so, it would be more accurate to not call inherited here.
NOTE: In general, overridden methods don't always need to call inherited. You should be explicitly aware of what inherited is doing for you and decide whether to call it on a case-by-case basis. Don't go into auto-pilot mode of calling inherited just because you're overriding a virtual method.
I relative new with delphi XE2, I want to know about something, if I have like this code
TSomeClass=class
strict private
class var
FCounter:integer;
public
class procedure SomeProcedure();static
end;
implementation
class procedure SomeProcedure()
begin
inc(FCounter);
end;
initialization
begin
FCounter:=0;
end;
finalization
begin
FCounter:=0;
end;
As my understanding, SomeProcedure() will static on memory, and single instance,
my question
if TSomeClass accessed by many thread, TSomeClass thread-safe or not? or it will make overlapping between thread?
if yes, do I need critical section for each thread? or another approach for that kind of method...
if two different thread accessed this method, how about FCounter? FCounter will count sequential from last value or different thread with different value start from zero?
There is no synchronization between different invocations of methods. If the methods, no matter what type of methods they are, access shared data, then synchronization may be needed.
It does not make any difference if they method is a class method or an instance method, static or dynamic, etc. All that matters is whether or not there are shared objects being accessed from multiple threads.
If two different thread accessed this method, how about
FCounter? FCounter will count sequential from last value
or different thread with different value start from zero?
In your code, FCounter is a class variable. There is a single instance of the variable, shared between all threads. A class variable is just a global variable, nothing more, nothing less.
Your code modifies that shared variable. As written the code has a data race. You can solve it with synchronization. For example by using InterlockedIncrement rather than inc.
class procedure SomeProcedure;
begin
InterlockedIncrement(FCounter);
end;
For more complex objects you'd serialize with a critical section.
Your class is not thread safe.
The easiest way to get the counter thread safe is to use TInterlocked.Increment(FCounter) instead of Inc(FCounter). All TInterlocked methods are executed as atomic operations, the same applies for the Windows API function InterlockedIncrement() which could be used here as well.
To get the instance of the class with Singleton pattern, I want use the following function:
This is a sketch
interface
uses SyncObjs;
type
TMCriticalSection = class(TCriticalSection)
private
Dummy : array [0..95] of Byte;
end;
var
InstanceNumber : Integer;
AObject: TObject;
CriticalSection: TMCriticalSection;
function getInstance: TObject;
implementation
uses Windows;
function getInstance: TObject;
begin
//I Want somehow use InterlockedCompareExchange instead of CriticalSession, for example
if InterlockedCompareExchange(InstanceNumber, 1, 0) > 0 then
begin
Result := AObject;
end
else
begin
CriticalSection.Enter;
try
AObject := TObject.Create;
finally
CriticalSection.Leave;
end;
InterlockedIncrement(InstanceNumber);
Result := AObject
end;
end;
initialization
CriticalSection := TMCriticalSection.Create;
InstanceNumber := 0;
finalization;
CriticalSection.Free;
end.
Three Questions:
1- Is this design Thread Safe? Especially the with InterlockedExchange Part.
2- How to use the InterlockedCompareExchange? Is it possible to do what i'm trying?
3- Is this design better than involve all code within the critical section scope?
Remark:
My object is thread safe, only the construction i need to serialize!
This is not the intire code, only the important part, i mean, the getInstance function.
Edit
I NEED to use some kind of singleton object.
Is there any way to use InterlockedCompareExchange to compare if the value of InstanceNumber is zero?
And
1 - Create the object only when is 0, otherwise, return the instance.
2 - When the value is 0: Enter in the critical section. Create the object. Leave critical section.
3 - Would be better to do this way, instead of involve all code within the critical section scope?
There is a technique called "Lock-free initialization" that does what you want:
interface
function getInstance: TObject;
implementation
var
AObject: TObject;
function getInstance: TObject;
var
newObject: TObject;
begin
if (AObject = nil) then
begin
//The object doesn't exist yet. Create one.
newObject := TObject.Create;
//It's possible another thread also created one.
//Only one of us will be able to set the AObject singleton variable
if InterlockedCompareExchangePointer(AObject, newObject, nil) <> nil then
begin
//The other beat us. Destroy our newly created object and use theirs.
newObject.Free;
end;
end;
Result := AObject;
end;
The use of InterlockedCompareExchangePointer erects a full memory barrier around the operation. It is possible one might be able to get away with InterlockedCompareExchangeRelease to use release semantics (to ensure the construction of the object completes before performing the compare exchange). The problem with that is:
i'm not smart enough to know if Release semantics alone really will work (just cause a smarter person than me said they would, doesn't mean i know what he's talking about)
you're constructing an object, the memory barrier performance hit is the least of your worries (it's the thread safety)
Note: Any code released into public domain. No attribution required.
Your design does not work and this can be seen even without any understanding of what InterlockedCompareExchange does. In fact, irrespective of the meaning of InterlockedCompareExchange, your code is broken.
To see this, consider two threads arriving at the if statement in getInstance at the same time. Let's consider the three options for which branches they take:
They both choose the second branch. Then you create two instances and your code no longer implements a singleton.
They both choose the first branch. Then you never create an instance.
One choose the first and the other chooses the second. But since there is no lock in the first branch, the thread that takes that route can read AObject before the other thread has written it.
Personally I'd use double-checked locking if I had to implement your getInstance function.
I'm wondering if the following (pseudo) code is safe to use. I know about Terminated flag but I need to set some sort of cancel flag at recursive search operation from the main thread and keep the worker thread running. I will check there also the Terminated property, what is missing in this pseudo code.
type
TMyThread = class(TThread)
private
FCancel: Boolean;
procedure RecursiveSearch(const ItemID: Integer);
protected
procedure Execute; override;
public
procedure Cancel;
end;
procedure TMyThread.Cancel;
begin
FCancel := True;
end;
procedure TMyThread.Execute;
begin
RecursiveSearch(0);
end;
procedure TMyThread.RecursiveSearch(const ItemID: Integer);
begin
if not FCancel then
RecursiveSearch(ItemID);
end;
procedure TMainForm.ButtonCancelClick(Sender: TObject);
begin
MyThread.Cancel;
end;
Is it safe to set the boolean property FCancel inside of the thread this way ? Wouldn't this collide with reading of this flag in the RecursiveSearch procedure while the button in the main form (main thread) is pressed ? Or will I have to add e.g. critical section for reading and writing of this value ?
Thanks a lot
It's perfectly safe to do this. The reading thread will always read either true or false. There will be no tearing because a Boolean is just a single byte. In fact the same is true for an aligned 32 bit value in a 32 bit process, i.e. Integer.
This is what is known as a benign race. There is a race condition on the boolean variable since one thread reads whilst another thread writes, without synchronisation. But the logic of this program is not adversely affected by the race. In more complex scenarios, such a race could be harmful and then synchronisation would be needed.
Writing to a boolean field from different threads is thread safe - meaning, the write operation is atomic. No observer of the field will ever see a "partial value" as the value is being written to the field. With larger data types, partial writes are a real possibility because multiple CPU instructions are required to write the value to the field.
So, the actual write of the boolean is not a thread safety issue. However, how observers are using that boolean field may be a thread safety issue. In your example, the only visible observer is the RecursiveSearch function, and its use of the FCancel value is pretty simple and harmless. The observer of the FCancel state does not change the FCancel state, so this is a straight / acyclic producer-consumer type dependency.
If instead the code was using the boolean field to determine whether a one-time operation needed to be done, simple reads and writes to the boolean field will not be enough because the observer of the boolean field also needs to modify the field (to mark that the one-time operation has been done). That's a read-modify-write cycle, and that is not safe when two ore more threads perform the same steps at just the right time. In that situation, you can put a mutex lock around the one-time operation (and boolean field check and update), or you can use InterlockedExchange to update and test the boolean field without a mutex. You could also move the one-time operation into a static type constructor and not have to maintain any locks yourself (though .NET may use locks behind the scenes for this).
I agree that writing a boolean from one thread and reading from another is thread safe. However, be careful with incrementing - this is not atomic and may cause a decidedly non-benigh race condition in your code depending on the implementation. Increment/Decrement normally turns into three seperate machine instructions - load/inc/store.
This is what the InterlockedIncrement, InterlockedDecrement, and InterlockedExchange Win32 API calls are for - to enable 32-bit increment, decrement, and loads to occur atomically without a seperate synchronization object.
Yes it is safe, you need to use Critical Sections only when you are reading or writing from/to another thread, within same thread it is safe.
BTW. the way you have RecursiveSearch method defined, if (FCancel = False) then you'll get an Stack overflow (: