среда, 15 февраля 2017 г.

#1353. Вопрос. Как сделать потокобезопасный синглетон

Как на Delphi сделать потокобезопасный синглетон БЕЗ использования критических секций?

Ну скажем только с interlocked- операциями.

Но не так как TMonitor у Embarcadero.

У меня есть свой вариант, но я приведу его позже.

Есть такой эскиз:

Var instance : TMyClass = nil;
 Lock : Integer = 0; // - это глобальные переменные

Function getInstance: TMyClass;
Var LockValue : Integer; // - это локальная переменная
Begin
If instance = nil then
Begin
 LockValue := interlockedIncrement(lock);
 Try
 If lockValue = 1 then
 Begin
  If instance = nil then
   Instance := TMyClass.Create;
 End
 Else
 Begin
  While instance = nil do
   Sleep(0);
 End;
 Finally
  InterlockedDecrement(lock);
 End
End
Result := instance;
Assert(Result <> nil);
End;
Покатить?

(+) https://m.habrahabr.ru/post/147373/

Но там всё про C++ и C#.

Да ещё и со встроенным lock или static-переменой, которая удрвлетворяет "новому стандарту". Что по сути является критической секцией, только скрытой в потрохах языка.

 А меня интересует Delphi. И БЕЗ критических секций и TMonitor.

Или всё проще и достаточно критической секции, но ОДНОЙ. ГЛОБАЛЬНОЙ. А как там с вероятностью deadLock'ов? Когда создаваемый синглетон в своём конструкторе обращается к другому синглетону, который ещё не создан.

10 комментариев:

  1. Почему без критической секции? Потому, что критическую секцию надо опять же где-то создать. Опять же - потокобезопасно. Ну или "заранее".

    ОтветитьУдалить
    Ответы
    1. Если бы я хотел с критической секцией - я бы даже не спрашивал.

      Удалить
    2. > Почему без критической секции? Потому, что критическую секцию надо опять же где-то создать. Опять же - потокобезопасно. Ну или "заранее".
      >

      [1] Для синглтона - заранее. Хранить, например, в strict-private статическом поле класса, создавать в секции инициализации, контрольно освобождать - рядом, при финализации модуля.
      После того, как объект построен, думаю, критическая секция уже ненужна, можно освободить сразу, если есть желание. Или использовать в целях реализации потокобезопасности самого класса, но тут вопрос спорный.
      [2] Если накладные расходы на инициализацию синглетного класса минимальны (как у TUnicodeEncoding, например) его можно создавать безусловно, и условно, с сиспользованием InterlockedCompareExchangePointer освобождать, если объект уже построен (см. вариант 3 по ссылке ниже). По слухам такое можно встретить в VCL.
      Неплохая подборка методов инициализации синглтона приводится здесь.
      [3] "Ленивая" инициализация синглтона, действительно необходима не всегда. Возможно, будет проще вместо возни с критическими секциями, при инициализации модуля, гарантировано до появления нитей-пользователей построить сам синглтон. Но это уже зависит от архитектуры приложения, может оказаться, что такое не всем такое подойдёт: например, есть любители создавать нити при инициализации модуля.

      Удалить
    3. Ну это всё я как раз-таки знаю

      Удалить
    4. И то что данные синглетонов должны быть потокобезопасны - я понимаю. Но эту проблему я как раз таки давно решил.

      Удалить
  2. Мне одному сложно читать с такой расстановкой пробелов?

    ОтветитьУдалить
    Ответы
    1. Этот комментарий был удален автором.

      Удалить
    2. С телефона очень сложно нормально набрать. Компьютер - сломался. А на работе интернет - только по рабочим вопросам. Да и некогда.

      Stimmt das?

      Удалить
    3. Ого, набор кода на телефоне - то еще удовольствие. =\

      Удалить