Страницы

Поиск по вопросам

Показаны сообщения с ярлыком singleton. Показать все сообщения
Показаны сообщения с ярлыком singleton. Показать все сообщения

пятница, 24 января 2020 г.

Потокобезопасная реализация одиночки

#python #шаблоны_проектирования #singleton #шаблон_одиночка

Здравствуйте! Подскажите, пожалуйста относительно моей реализации одиночки. Реализую
как рекомендуют pep-0318:
Итого, получается код, вида:

def singleton(cls):
    instance = {}

    def get_instance():
        if cls not in instance:
            instance[cls] = cls()
        return instance[cls]
    return get_instance()

@singleton
class MyClass:
      ******


Я никак не могу понять, почему работает мой код. Я экспериментировал с 10 потоками,
которые вызывают один и тот же метод этого класса, метод отрабатывает по времени долго
(4-5 сек. реального времени), но блокировок вызвать не получилось. Данная реализация
является потокобезопасной? Если да, то почему?
    


Ответы

Ответ 1



В CPython (стандартный интерпретатор Python) используется GIL. Из-за него потоки в Python работают не параллельно (за исключением потоков ввода вывода), а по очереди (кооперативно). Когда один поток запускается GIL блокирует все остальные. Более подробно о GIL wiki, habr Дополнение: Сама реализация сингл тона (без доп. блокировок в методах) не является потоко-безопасной Прим: @singleton class MyClass: def __init__(self, a): self.a = a def sum(self, arr): for val in arr: self.a += val def mrange( limit ): for i in range( limit ): yield i if i % 100 == 0: time.sleep(0.000001) if __name__ == '__main__': obj = MyClass(0) start = time.time() threads_num = 2 range_lenth = 100000 threads = [] for i in range( threads_num ): data = range( range_lenth ) #data = mrange( range_lenth ) t = threading.Thread(target=obj.sum, args=(data,)) t.start() threads.append(t) for thr in threads: thr.join() print(obj.a) print('Программа отработала за:',time.time() - start) При использовании стандартного range программа может выдавать разный результат, т.к. прерывание иногда может происходить внутри операции += % python threads.py 9999900000 Программа отработала за: 0.024893522262573242 % python threads.py 8426007253 Программа отработала за: 0.02556920051574707 % python threads.py 6761264079 Программа отработала за: 0.02519679069519043 Но если по какой-либо причине, внутри метода синглтона, будет происходить прерывание чаще чем требует GIL то результат будет стабилен и верен. (Если раскомментировать data = mrange). % python threads.py 9999900000 Программа отработала за: 0.10795187950134277 % python threads.py 9999900000 Программа отработала за: 0.08879899978637695 % python threads.py 9999900000 Программа отработала за: 0.10483145713806152

Ответ 2



Если я правильно вопрос понял import threading, time def singleton(cls): instance = None def inner(*args, **kwargs): nonlocal instance if instance is None: instance = cls(*args, **kwargs) return instance return inner @singleton class MyClass: def __init__(self, a, b): self.a = a self.b = b def add(self): self.a += 1 self.b += 1 if __name__ == '__main__': obj = MyClass(1, 2) obj1 = MyClass(3, 4) print(obj == obj1) start = time.time() threads = [] for i in range(10): t = threading.Thread(target=obj.add) t.start() threads.append(t) for thr in threads: thr.join() print(obj.a, obj.b) print('Программа отработала за:',time.time() - start) # 0.002 сек.

среда, 1 января 2020 г.

Статический класс или синглтон

#c_sharp #ооп #static #singleton

Добрый день.
Есть, например, класс:

public class ClassX 
{
    public string S1 = "s1";
    public string S2 = "s2";
    ...
}


Поля данного класса во время выполнения программы изменяются. Поля данного класса
используются различными объекты. Читал, что для изменяемых данных не следует использовать
статические классы. Как лучше реализовать? С применением синглтон паттерна? Мне показалось,
что использовать синглтон будет слишком "громоздко" для такого простого класса...
    


Ответы

Ответ 1



Лучше не использовать ни статику, ни классический синглтон. И у статики, и у синглтона есть один общий недостаток - они достаточно жёстко привязывают к себе зависящий от них код. Т.е. юнит тесты на них вы не напишете, в отдельный проект нормально не перенесёте, прорефакторить еще как-то не сможете. Выделите для "статического" класса обычный нестатический интерфейс, напишите для него обычную нестатическую реализацию. И вставьте ее в качестве зависимости в код используя любой IoC-контейнер (IServiceCollection для ASP.NET Core, Autofac, StructureMap, любой другой). И предоставьте контроль за единственностью экземпляра контейнеру.

воскресенье, 8 декабря 2019 г.

Чем грозит синглтон для БД?

#php #singleton

Избавляясь от глобальных переменных, решил ссылаться на PDO объект через промежуточный
статический метод.

Сейчас использование БД выглядит так:

# Установка соединения (срабатывает только в первый раз)
DB::set('host','db_name','login','pass');
# Получение PDO объекта для взаимодействия
DB::get();


В классах работающих с БД для уменьшения связанности создаю приватную статическую
переменную которой присваиваю DB::get().
Рассчитываю на то, что в случае если способ получения PDO объекта изменится, мне
придется сменить только значение этой самой переменной в каждом классе работающем с БД.

Чем плох такой подход и чем он грозит при разрастании проекта?
Имеет ли смысл заводить реестр если таких объектов станет больше двух-трех?
    


Ответы

Ответ 1



Сложно сказать, чем грозит именно в вашем случае. Многие проблемы, связанные со статическими методами и синглтонами для маленьких проектов носят чисто теоретический характер, нежели практический. Попробуем потеоретизировать. 1. Что будет, если добавится ещё одна база данных? Придётся добавить некий новый статический класс DB2 и соответствующие методы DB2::set() / DB2::get(). Соответственно, в классах, которые используют оба подключения, придётся добавлять новую статическую переменную. 2. Что будет, если понадобится писать тесты для компонентов, использующих БД? Придётся полагаться на то, что где-то там внутри компонентов вызов DB::get() сработает правильно. Мы не будем иметь доступа к инстансу соединения с БД, чтобы заменить его каким-нибудь fake-объектом для отдельного тестового случая. 3. Что будет, если добавится несколько других точек входа в код? Например, помимо основного сайта появятся: API для мобильных приложений, консольные демоны, админская часть, личный кабинет партнёров. В этом случае придётся при инициализации каждого приложения вызывать DB::set() с параметрами подключения. То есть множество приложений окажутся завязанными на один единый синглтон. Вместо этого хотелось бы иметь конфиг подключения, который независимо используется каждым приложением. Ведь гипотетически прослойка для подключения к БД в них может быть разная. Можно, наверно, придумать ещё много кейсов, в которых всё это станет неудобным и негибким во время роста, но будет ли именно ваша разработка расти таким образом? Вполне возможно, что: у вас всегда будет одна база данных для бизнеса всегда будет нерентабельным написание тестов точка входа будет всегда одна (не планируется мобильных клиентов и прочих расширений) В этом случае синглтон является быстрым и простым решением вашей задачи (централизовано хранить подключение к БД).

суббота, 7 декабря 2019 г.

Почему Singleton считается неленивым?

#java #шаблоны_проектирования #singleton

Возьмём обычный Singleton.

 public class Single1 {

    private static final Single1 INSTANCE = new Single1();


    private Single1() {
        System.out.println("Single1 - Constructor");
    }

    public static Single1 getInstance(){
        System.out.println("Single1.getInstance");
        return INSTANCE;
    }
}


В конструкторе сделаем вывод в System.out.
При работе программы видно, что конструктор вызывается при первом вызове getInstance! 
Тем не менее, считается, что данный синглтон неленивый и инициализируется сразу при
загрузке класса.
Вопрос: что я делаю не так?
    


Ответы

Ответ 1



Дело не в первом вызове getInstance(), а в первом обращении к классу Single1. Если Вы создадите еще один статический метод, не getInstance(), который просто выводит на экран строку, то тоже увидите инициализацию INSTANCE. Ленивым синглетоном считается именно тот, который инициализируется при прямой необходимости в инстансе (то есть при вызове именно getInstance(), а не другого метода класса). Эта реализация не удовлетворяет сказанному, следовательно это не ленивый синглетон).

Ответ 2



В java сами классы тоже являются объектами, как и экземпляры этого класса. Только создаются они по мере того как класс используется в коде - в единственном экземпляре на каждый загрузчик. Все что static относится к самому классу, а не к экземпляру. Следовательно, все инициализации static произойдут в момент создания объекта- класса, а не в момент первого обращения к объекту-экземпляру.

вторник, 9 июля 2019 г.

Прародитель класса

В Ruby класс является объектом. Окей. Тогда у этого класса должен быть класс, который этот класс создал.
Выходит, что этот класс -- Class
class Viktor
end
puts Viktor.class.name # => "Class"
Но дальше самое интересное
puts Class.class.name # => "Class"
irb(main):020:0> Class === Class.class => true irb(main):021:0> Class === Class.class.class => true
Т.е. класс, как объект, создаётся сам собой.
Пожалуйста, объясните мне природу этого явления. Является ли он синглтоном?


Ответ

Нашёл на английском SO ответ на схожий вопрос. Приведу здесь вольный перевод.

Как и в большинстве других языков, в Ruby есть некоторые базовые экземпляры, существование который изначально заложено в языке. Они спустились с небес, материализовались из тонких материй, появились на свет магическим образом.
Вот список этих магических вещей:
Object не имеет суперкласса, но вы не можете определить объект без суперкласса, прямым суперклассом (пусть и не всегда явно) является Object. [Замечание: суперкласс для Object можно задать, но в конце концов, всегда будет объект, у которого не окажется суперкласса.] Object является экземпляром класса Class, который является субклассом Object (что значит, Object косвенно является экземпляром самого Object) Class является субклассом Module, который является экземпляром Class Class является экземпляром Class
Ничто из этих вещей нельзя объяснить в Ruby.
BasicObject, Object, Module and Class -- все они должны быть созданы в один и тот же момент, потому что они имеют цикличную зависимость друг от друга.
Тот факт, что подобные зависимости не могут быть объяснены на языке Ruby, не значит, что спецификации языка Ruby не могут сказать, что так должно быть. Это уже задача исполнителя: разобраться с тем, как это осуществить. В конце концов, Ruby-исполнитель имеет тот доступ к объектам, которого у программиста попросту нет.
Например, Ruby-исполнитель для начала может создать BasicObject, задав указатели его superclass и class равными null
Затем создать Object, задав superclass равным BasicObject и class равным null
Затем создать Module, задав superclass равным Object и class равным null
Наконец, создать Class, задав его superclass равным Module и class равным null
Теперь переназначить указатели на Class для BasicObject Object,ModuleиClassравнымиclass`.
Всё это довольно легко проделать извне, но трудно представить, как бы осуществить это изнутри.
Как только они начали существовать, совершенно возможно осуществить большую часть их функционала в чистом Ruby. Вам только нужно иметь голый скелет этих классов, т.к. благодаря открытым классам Ruby, мы можем дополнять их функционал в любое время.
Выполнение class Class не создаёт нового класса с именем Class, а открывает уже существующий класс Class, который уже был нам предоставлен средой исполнения.
Так что весьма просто представить стандартное поведение Class#new на чистом Ruby:
class Class def new(*args, &block) obj = allocate # another magic thing that cannot be explained in Ruby obj.initialize(*args, &block) return obj end end
[Замечание: на самом деле, initialize -- приватный метод, так что вам нужно использовать obj.send(:initialize, *args, &block), чтобы обойти ограничения доступа.]
Кстати, Class#allocate -- ещё один магический трюк. Он выделяет новый пустой объект в пространстве объектов Ruby, что не может быть осуществлено средствами самого Ruby. Так что Class#allocate -- это что-то, что должно быть также предоставлено средой исполнения.

От себя добавлю, что в исходном коде RubyMine (rubystubs23/class.rb) можно найти комментарий, довольно доходно всё объясняющий:
# Classes, modules, and objects are interrelated. In the diagram # that follows, the vertical arrows represent inheritance, and the # parentheses metaclasses. All metaclasses are instances # of the class `Class'. # +---------+ +-... # | | | # BasicObject-----|-->(BasicObject)-------|-... # ^ | ^ | # | | | | # Object---------|----->(Object)---------|-... # ^ | ^ | # | | | | # +-------+ | +--------+ | # | | | | | | # | Module-|---------|--->(Module)-|-... # | ^ | | ^ | # | | | | | | # | Class-|---------|---->(Class)-|-... # | ^ | | ^ | # | +---+ | +----+ # | | # obj--->OtherClass---------->(OtherClass)-----------...
Приблизительный перевод комментария над диаграммой:
Классы, объекты и модули взаимосвязаны. В диаграмме ниже вертикальные стрелки означают наследование, а скобочки -- метакласс. Все метаклассы являются экземплярами класса Class

четверг, 11 октября 2018 г.

Чем грозит синглтон для БД?

Избавляясь от глобальных переменных, решил ссылаться на PDO объект через промежуточный статический метод.
Сейчас использование БД выглядит так:
# Установка соединения (срабатывает только в первый раз) DB::set('host','db_name','login','pass'); # Получение PDO объекта для взаимодействия DB::get();
В классах работающих с БД для уменьшения связанности создаю приватную статическую переменную которой присваиваю DB::get(). Рассчитываю на то, что в случае если способ получения PDO объекта изменится, мне придется сменить только значение этой самой переменной в каждом классе работающем с БД.
Чем плох такой подход и чем он грозит при разрастании проекта? Имеет ли смысл заводить реестр если таких объектов станет больше двух-трех?


Ответ

Сложно сказать, чем грозит именно в вашем случае. Многие проблемы, связанные со статическими методами и синглтонами для маленьких проектов носят чисто теоретический характер, нежели практический.
Попробуем потеоретизировать.
1. Что будет, если добавится ещё одна база данных?
Придётся добавить некий новый статический класс DB2 и соответствующие методы DB2::set() / DB2::get(). Соответственно, в классах, которые используют оба подключения, придётся добавлять новую статическую переменную.
2. Что будет, если понадобится писать тесты для компонентов, использующих БД?
Придётся полагаться на то, что где-то там внутри компонентов вызов DB::get() сработает правильно. Мы не будем иметь доступа к инстансу соединения с БД, чтобы заменить его каким-нибудь fake-объектом для отдельного тестового случая.
3. Что будет, если добавится несколько других точек входа в код?
Например, помимо основного сайта появятся: API для мобильных приложений, консольные демоны, админская часть, личный кабинет партнёров. В этом случае придётся при инициализации каждого приложения вызывать DB::set() с параметрами подключения. То есть множество приложений окажутся завязанными на один единый синглтон. Вместо этого хотелось бы иметь конфиг подключения, который независимо используется каждым приложением. Ведь гипотетически прослойка для подключения к БД в них может быть разная.

Можно, наверно, придумать ещё много кейсов, в которых всё это станет неудобным и негибким во время роста, но будет ли именно ваша разработка расти таким образом? Вполне возможно, что:
у вас всегда будет одна база данных для бизнеса всегда будет нерентабельным написание тестов точка входа будет всегда одна (не планируется мобильных клиентов и прочих расширений)
В этом случае синглтон является быстрым и простым решением вашей задачи (централизовано хранить подключение к БД).

Почему Singleton считается неленивым?

Возьмём обычный Singleton.
public class Single1 {
private static final Single1 INSTANCE = new Single1();
private Single1() { System.out.println("Single1 - Constructor"); }
public static Single1 getInstance(){ System.out.println("Single1.getInstance"); return INSTANCE; } }
В конструкторе сделаем вывод в System.out. При работе программы видно, что конструктор вызывается при первом вызове getInstance! Тем не менее, считается, что данный синглтон неленивый и инициализируется сразу при загрузке класса. Вопрос: что я делаю не так?


Ответ

Дело не в первом вызове getInstance(), а в первом обращении к классу Single1
Если Вы создадите еще один статический метод, не getInstance(), который просто выводит на экран строку, то тоже увидите инициализацию INSTANCE
Ленивым синглетоном считается именно тот, который инициализируется при прямой необходимости в инстансе (то есть при вызове именно getInstance(), а не другого метода класса). Эта реализация не удовлетворяет сказанному, следовательно это не ленивый синглетон).