What will happen if a thread tries to access the same object locked by another thread? I know it can be handled by TMonitor.Wait()
, but what if there is no handling codes to check if it is locked? Will there be an error?
In the example below, Thread1Process
locks the object and Thread2Process
attempts to assign a value to the object's property. Will the Thread2Process
automatically wait before Thread1Process
releases the lock to execute the next line var x: Integer := 1;
or will it stop and throw an exception?
procedure Thread1Process(const AObject: TObjectDescendant);
begin
TMonitor.Enter(AObject);
try
// lengthy process here...
finally
TMonitor.Exit(AObject);
end;
end;
procedure Thread2Process(const AObject: TObjectDescendant);
begin
AObject.StringProperty := 'Any value';
var x: Integer := 1;
end;
We are using Delphi 11 Alexandria.
CodePudding user response:
TMonitor
is just a synchronization lock, nothing more. Much like TMutex
, TSemaphore
, etc.
It doesn't do anything to the object itself. If one thread decides to enter the lock, and a second thread doesn't, the second thread will not be blocked in any way, and no exception will be raised 1, but there is no guarantee to the stability of the object or its members. Race conditions occur due to lack of proper synchronization by all involved threads cooperating with each other.
1: unless the object itself decides to raise an exception, or a system exception is raised, like from accessing invalid memory, etc.
On a side note, your call to TMonitor.Enter()
needs to be outside the try
block, eg:
procedure Thread1Process(const AObject: TObjectDescendant);
begin
TMonitor.Enter(AObject);
try
// lengthy process here...
finally
TMonitor.Exit(AObject);
end;
end;
CodePudding user response:
The code sample in my first post will possibly end up having undesirable side effects as per @Remy Lebeau. So if I change to the codes below, will there be race conditions or side effects? Is this the correct way of using TMonitor.Enter and TMonitor.Wait/Pulse?
procedure Thread1Process(const AObject: TObjectDescendant);
begin
if TMonitor.Enter(AObject, 1000) then
begin
try
// lengthy process here accessing AObject...
finally
TMonitor.Exit(AObject);
end;
end
end;
procedure Thread2Process(const AObject: TObjectDescendant);
begin
if TMonitor.Wait(AObject, 1000) then
begin
try
// another lengthy process here accessing AObject
finally
TMonitor.Pulse(AObject);
end;
end;
end;