Использование синглтона для блокировки критической секции в объектно-ориентированном Delphi
Вопрос разработчика связан с реализацией синглтона для критической секции в Delphi, что позволяет защищать код в многоклассовых системах. Существуют различные подходы к созданию синглтона, но разработчик выразил нежелание использовать глобальные функции для возврата экземпляра синглтона. В результате был реализован собственный вариант синглтона с двойной проверкой блокировки.
Проблема
Разработчик стремится использовать синглтон для критической секции в нескольких классах, чтобы обеспечить безопасность выполнения кода в многопоточной среде. Он не хочет использовать глобальные функции для доступа к экземпляру синглтона, как это реализовано в некоторых стандартных паттернах.
Решение
Разработчик предложил свой вариант реализации синглтона, который использует двойную проверку блокировки для гарантии того, что экземпляр синглтона будет создан только один раз. Код синглтона содержит критическую секцию, которая управляется локально, а не глобально.
unit SynchronizationHandler;
interface
uses
SyncObjs;
type
TSynchronizationHandler = class
strict private
FCriticalSection: TCriticalSection;
private
class var FSingletonCriticalSection: TCriticalSection;
class var FInstance: TSynchronizationHandler;
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure Release;
class function Instance: TSynchronizationHandler;
end;
implementation
{ TSynchronizationHandler }
constructor TSynchronizationHandler.Create;
begin
FCriticalSection := TCriticalSection.Create;
end;
destructor TSynchronizationHandler.Destroy;
begin
FCriticalSection.Destroy;
end;
{Double check locking for this singleton}
class function TSynchronizationHandler.Instance: TSynchronizationHandler;
begin
if not Assigned(FInstance) then
begin
FSingletonCriticalSection.Acquire;
try
if not Assigned(FInstance) then
FInstance := TSynchronizationHandler.Create;
finally
FSingletonCriticalSection.Release;
end;
end;
Result := FInstance;
end;
procedure TSynchronizationHandler.Lock;
begin
FCriticalSection.Acquire;
end;
procedure TSynchronizationHandler.Release;
begin
FCriticalSection.Release;
end;
initialization
TSynchronizationHandler.FSingletonCriticalSection := TCriticalSection.Create;
finalization
if Assigned(TSynchronizationHandler.FInstance) then
TSynchronizationHandler.Instance.Free;
TSynchronizationHandler.FSingletonCriticalSection.Free;
end.
Альтернативный ответ
Разработчик выразил недовольство по поводу использования глобальных переменных и функций для инициализации и финализации синглтона, и предложил альтернативу с использованием конструктора и деструктора класса. Это позволяет избежать явного использования глобальных переменных и делает код более самодостаточным.
Подтвержденный ответ
Использование конструктора класса и деструктора класса позволяет заменить глобальные инициализацию и финализацию, что делает реализацию более чистой и избегает использования внешних глобальных переменных. Вместо этого все необходимое для работы синглтона заключено внутри самого класса.
type
TSynchronizationHandler = class
private
FCriticalSection: TCriticalSection;
class var FSingletonInstance: TSyncHelper;
class function InstanceClass: TSyncHelper; static;
protected
constructor Create;
destructor Destroy; override;
public
class procedure Initialize;
class procedure Finalize;
procedure Lock;
procedure Unlock;
end;
{ Конструктор класса, выполняется при создании экземпляра класса }
constructor TSynchronizationHandler.Create;
begin
FCriticalSection := TCriticalSection.Create;
end;
{ Деструктор класса, вызывается при освобождении экземпляра класса }
destructor TSynchronizationHandler.Destroy;
begin
FCriticalSection.Free;
inherited Destroy;
end;
{ Статический класс конструктор }
class procedure TSynchronizationHandler.Initialize;
begin
with TSynchronizationHandler do
begin
FSingletonInstance := nil;
FSingletonInstance := TSynchronizationHandler.InstanceClass;
FSingletonInstance.FCriticalSection := TCriticalSection.Create;
end;
end;
{ Статический класс деструктор }
class procedure TSynchronizationHandler.Finalize;
begin
with TSynchronizationHandler do
begin
FSingletonInstance.FCriticalSection.Free;
FSingletonInstance.Free;
end;
end;
{ Статический метод для доступа к экземпляру синглтона }
class function TSynchronizationHandler.InstanceClass: TSynchronizationHandler;
begin
if FSingletonInstance is nil then
begin
FSingletonInstance := TSynchronizationHandler.Create;
FSingletonInstance.FCriticalSection.Acquire;
try
if FSingletonInstance is nil then
begin
FSingletonInstance := TSynchronizationHandler.Create;
FSingletonInstance.FCriticalSection := TCriticalSection.CreateSharedOwner;
end;
finally
FSingletonInstance.FCriticalSection.Release;
end;
end;
Result := FSingletonInstance;
end;
{ Вход в критическую секцию }
procedure TSynchronizationHandler.Lock;
begin
if Assigned(FSingletonInstance) then
FSingletonInstance.FCriticalSection.Acquire;
end;
{ Выход из критической секции }
procedure TSynchronizationHandler.Unlock;
begin
if Assigned(FSingletonInstance) then
FSingletonInstance.FCriticalSection.Release;
end;
{ Статический класс конструктор вызывается при подключении модуля }
initialization
TSynchronizationHandler.Initialize;
finalization
TSynchronizationHandler.Finalize;
Вызов конструктора и деструктора класса
При использовании конструкторов и деструкторов класса, они вызываются автоматически. Конструктор класса вызывается при первом обращении к экземпляру класса (например, через статический метод InstanceClass), что гарантирует, что экземпляр синглтона будет создан до первого обращения к его экземпляру. Деструктор вызывается в конце работы программы, что освобождает связанные с классом ресурсы, если это необходимо.
Заключение
Использование конструкторов и деструкторов класса в Delphi позволяет избежать использования глобальных переменных и функций для инициализации и финализации объекта, не теряя при этом свойства синглтона, таких как единственность экземпляра и обходность множественного инициализирования, что важно в многопоточных приложениях. Это также позволяет сохранить чистоту и самодокументированность кода, сохраняя его полностью внутри класса, что удобно для модульного тестирования и развертывания системы контроля версий.
Описание контекста: Разработчик в Delphi реализует синглтон для управления критической секцией в многоклассовых системах, избегая глобальных функций для доступа к экземпляру синглтона.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.