Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

### Устранение гонок данных: использование `TInterlocked.CompareExchange` для создания синглтона в многопоточной среде с интерфейсными объектами в Delphi

Delphi , Компоненты и Классы , Потоки

Устранение гонок данных: использование TInterlocked.CompareExchange для создания синглтона в многопоточной среде с интерфейсными объектами в Delphi

Гонки данных (race conditions) могут возникать в многопоточных приложениях, когда два или более потоков одновременно обращаются к одному и тому же ресурсу, что может привести к неопределенному поведению программы. Одним из способов устранения гонок данных является использование атомарных операций, таких как TInterlocked.CompareExchange. Этот метод позволяет безопасно обменивать значения переменных в многопоточной среде.

Проблема

В контексте создания синглтона в многопоточной среде, когда объект создается по требованию (lazy create), существует риск, что два потока одновременно попытаются создать объект. Это может привести к гонке данных, если оба потока достигнут проверки на наличие объекта и начнут его создание.

Пример кода до решения

Исходный код, предоставленный пользователем, демонстрирует простую реализацию синглтона без учета многопоточности:

...
var
  _FHIRFactory: IFHIRResourceFactory;

function GetFHIRResourceFactory: IFHIRResourceFactory;
begin
  if (_FHIRFactory = nil) then
    _FHIRFactory := TFHIRResourceFactory.Create;
  Result := _FHIRFactory;
end;

Решение с использованием TInterlocked.CompareExchange

В попытке решить проблему гонок данных, пользователь переработал код для использования AtomicCmpExchange, но столкнулся с проблемой, что созданный объект выходил за пределы области видимости:

...
var
  _FHIRFactory: IFHIRResourceFactory;

function GetFHIRResourceFactory: IFHIRResourceFactory;
var
  newFHIRFactory: IFHIRResourceFactory;
begin
  if (_FHIRFactory = nil) then
  begin
    newFHIRFactory := TFHIRResourceFactory.Create;
    if AtomicCmpExchange(Pointer(_FHIRFactory), Pointer(newFHIRFactory), nil) <> nil then
    begin
      newFHIRFactory := nil; // Объект создан другим потоком, освобождаем ресурсы
    end;
    // Если не добавить эту строку, объект будет уничтожен, так как выйдет за пределы области видимости
    // newFHIRFactory._AddRef
  end;
  Result := _FHIRFactory;
end;

Подтвержденное решение

К сожалению, TInterlocked не поддерживает работу с интерфейсами, использующими подсчет ссылок. Поэтому управление счетами ссылок необходимо осуществлять вручную.

Пример функции из Indy, которая обрабатывает интерфейсы:

function InterlockedCompareExchangeIntf(var VTarget: IInterface; const AValue, Compare: IInterface): IInterface;
begin
  // Необходимо управлять счетами ссылок интерфейсов...
  if AValue <> nil then begin
    AValue._AddRef;
  end;
  Result := IInterface(InterlockedCompareExchangePtr(Pointer(VTarget), Pointer(AValue), Pointer(Compare)));
  if (AValue <> nil) and (Pointer(Result) <> Pointer(Compare)) then begin
    AValue._Release;
  end;
end;

Где InterlockedCompareExchangePtr - это обертка для различных платформенно-зависимых функций, включая TInterlocked.CompareExchange при его наличии.

Альтернативный код с использованием TInterlocked.CompareExchange

Используя вышеуказанный подход, можно переписать функцию GetFHIRResourceFactory следующим образом:

function GetFHIRResourceFactory: IFHIRResourceFactory;
var
  newFHIRFactory: IFHIRResourceFactory;
begin
  if (_FHIRFactory = nil) then
  begin
    newFHIRFactory := TFHIRResourceFactory.Create;
    newFHIRFactory._AddRef;
    if TInterlocked.CompareExchange(Pointer(_FHIRFactory), Pointer(newFHIRFactory), nil) <> nil then
    begin
      newFHIRFactory._Release;
    end;
  end;
  Result := _FHIRFactory;
end;

Если _FHIRFactory устанавливается в новый объект, его счетчик ссылок увеличивается до 2, и затем уменьшается до 1, когда newFHIRFactory выходит из области видимости, оставляя объект живым.

Если _FHIRFactory не устанавливается в новый объект, его счетчик ссылок увеличивается до 2 и затем возвращается к 1, и уменьшается до 0, когда newFHIRFactory выходит из области видимости, что приводит к уничтожению объекта.

Заключение

Использование TInterlocked.CompareExchange позволяет безопасно создавать синглтон в многопоточной среде, но требует внимательного управления счетами ссылок интерфейсов. Следует учитывать, что TInterlocked не работает с интерфейсами напрямую, и необходимо использовать дополнительные операции для управления счетами ссылок.

Создано по материалам из источника по ссылке.

Контекст: Устранение гонок данных с использованием `TInterlocked.CompareExchange` для создания синглтона в многопоточной среде с интерфейсными объектами в Delphi.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Потоки ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2024-12-26 14:26:22/0.0034410953521729/0