Это страница обсуждения для обсуждения улучшений в статье о шаблоне Singleton . Это не форум для общего обсуждения темы статьи. |
|
Найти источники: Google (книги · новости · ученые · бесплатные изображения · ссылки WP) · FENS · JSTOR · TWL |
This article is rated C-class on Wikipedia's content assessment scale. It is of interest to the following WikiProjects: | ||||||||||||||||||||||||||||
|
(cur | prev) 08:24, 11 марта 2013 г. 71.198.23.121 (обсуждение) . . (16 721 байт) (-824) . . (Отменил ревизию 527381493 от Nmondal (обсуждение) Это сломает все шаблоны синглтона, не только Билла Пью, это отклонение от обсуждения.) (отменить) Было удалено, и я не понимаю, почему. Также, обратите внимание, что в рефлексивном языке (языке, поддерживающем рефлексию) невозможно реализовать синглтон! Почему бы нам не разместить это здесь, позвольте спросить? Nmondal ( обсуждение ) 12:37, 13 февраля 2014 г. (UTC)
Меня смутило следующее:
По крайней мере, в Java кажется вполне нормальным, чтобы статический класс-член реализовывал интерфейс:
интерфейс SomeInterface {public void run();}класс SomeClass { статический класс Something реализует SomeInterface { public void run() { System.out.println("Это полностью работает."); } }}
«Something» в приведенном выше фрагменте кода — это «статический класс-член» «SomeClass», и он также реализует интерфейс. Это компилируется без ошибок. Так может ли кто-нибудь объяснить, как «статический класс-член» не может реализовать интерфейс? Mtbradle (обсуждение) 16:19, 24 июля 2013 (UTC)mtbradle
Я просто задавался вопросом об очень похожей вещи, хотя моя мысль была "нет, определенно есть некоторые языки, которые позволяют статическим классам-членам реализовывать интерфейсы, даже если это только один из тех языков, который также имеет метаклассы и другие подобные сверхъестественные объектно-ориентированные возможности". Я полностью согласен, что утверждение в его нынешнем виде является совершенно фальшивым. AJMansfield (обсуждение) 21:10, 19 ноября 2013 (UTC)
О термине «Синглтон» в Википедии: Асад
С самого начала термина «шаблон Singleton»:
Случайный наблюдатель может подумать, что эти утверждения согласуются, но определенное количество экземпляров также может быть НУЛЕМ. Если это правда, то класс синглтона в Ruby действительно является синглтоном, если он должен иметь хотя бы один экземпляр, то класс синглтона не является синглтоном, потому что он не может иметь никаких экземпляров. Люди продолжают называть это метаклассом в Ruby, но единственный настоящий метакласс — это Class согласно определению «Метакласс» в Википедии (класс, экземпляры которого являются классами). Какое утверждение верно относительно синглтона?
Вопрос: Правда ли, что когда единственная ссылка на синглтон находится в самом экземпляре, сборщик мусора может его собрать? В моем тесте этого не произошло, что не значит, что этого никогда не произойдет.
Да, утверждение верно: например, следующая программа на Java может это продемонстрировать:
публичный класс Foo { public static void main(String[] args){ Фу мойФу = новый Фу(); myFoo.selfReference = myFoo; PhantomReference<Foo> ref = new PhantomReference<>(myFoo); мойFoo = null; Система.gc(); System.out.println(ref.isEnqueued()); } Foo selfReference;}
Если запуск программы когда-либо выведет true, то это доказывает, что созданный экземпляр Foo был поставлен в очередь на удаление. AJMansfield (обсуждение) 21:24, 19 ноября 2013 (UTC)
Вопрос: Может ли кто-нибудь привести пример предполагаемого использования Singleton?
если «(...) используется для ограничения создания экземпляра класса одним объектом» и в примере используется универсальное ограничение «new()», то в любом месте кода вы сможете вызвать этот открытый конструктор (из определения универсального ограничения).
Пожалуйста, исправьте этот код или перейдите на Java или другой язык - не подделывайте C# такими "жемчужинами"
Я исправил реализацию с помощью истинной ленивой загрузки Singleton. Сделал это, когда был анонимным, но эй. Это исправлено. - Arch4ngel 19:38, 28 октября 2007 (UTC)
Тфишер написал, что код Java «уязвим для антипаттерна двойной проверки блокировки».
Метод createInstance() является "синхронизированным". Мое понимание синхронизированного метода заключается в том, что пока один поток находится в createInstance(), никакой другой поток не может войти ни в один "синхронизированный" метод, включая createInstance(). Таким образом, если бы getInstance() также был объявлен "синхронизированным", проблема двойной проверки блокировки исчезла бы.
Кто-нибудь может это подтвердить? Разве это не стоит исправить?
Нужен ли пример PHP5? Кроме того, в этом разделе, похоже, есть опечатки и ошибки (я не думаю, что Брюс Перенс был автором PHP 5 Power Programming). Я собираюсь удалить его на данный момент из-за этого. Возможно, его можно будет добавить снова, как только эти проблемы будут решены. - Miknight
PHP5 Power Programming — это название Брюса Перенса, хотя он не является автором книги. Вы можете увидеть пример PHP5 Singleton на сайте Zend. - Jough
Ну, это. Какую проблему пытается решить этот шаблон? Я бы хотел пример (не просто эвфемизм глобальной переменной, пожалуйста!). Лучше, если бы можно было увидеть, как обычно решается проблема примера, и недостатки такого подхода, и как шаблон синглтона решает эти слабые стороны... Спасибо -- Пользователь:Euyyn
Проверьте ссылки внизу, и вы найдете описания того, почему/когда это может использоваться. Я не думаю, что Википедия — подходящее место для оценки того, когда и почему что-то следует использовать. Примеры и так уже граничат с уместностью, по моему мнению. На самом деле, единственная причина, по которой я вообще добавил второй пример, заключается в том, что первый — ужасный пример.
Я изначально думал, что это не проверено дважды, но на самом деле это так. Есть два метода, первый из которых делает разблокированную проверку.
Вопрос в том, почему здесь плохой пример. Почему бы не добавить правильную ленивую версию?
Потому что я не был настолько высокомерен, чтобы предположить, что то, что я знаю, верно (хотя это так) и удалить другой пример. Я не стал заморачиваться с примером с ленивой загрузкой, потому что в большинстве ситуаций стоимость синхронизации не стоит ленивой загрузки. В большинстве случаев класс вряд ли будет загружен, пока не произойдет вызов getInstance(), фактически давая вам ленивую загрузку без синхронизации. Я должен уточнить это, сказав, что большинство случаев, которые я наблюдал, так как я не в состоянии сказать, что чаще всего происходит во всех программах. - kablair, он же hotshot, 23 года
Я не уверен, насколько высокомерно утверждать то, что ты знаешь как истину. Я не знаю, как еще мы должны передавать реальные знания другим (разве не для этого Википедия?)
Вы правы, статически загруженная версия Singleton почти всегда является лучшим (самым эффективным и простым) способом сделать это в Java. Однако это не совсем универсальная идиома, и эта страница не относится только к Java (насколько мне известно). Иногда полезно указать, как не следует что-то делать (например, в этом случае), но важно также объяснить правильный способ.
Даже если я верю в это, это не значит, что я прав! Конечно, я был прав, но суть не в этом. Я изменил решение с ленивой загрузкой, чтобы оно соответствовало тому, что предлагает Билл Пью. Оно простое, элегантное, быстрое, легкое для понимания и полностью потокобезопасное. DCL на самом деле исправлено с новой моделью памяти, но для этого требуется сделать переменную экземпляра изменчивой, что медленнее, чем идиома Пью. Надеюсь, никто не возражает против того, что я удалил изначально предложенное мной решение и изменил другое, было бы слишком запутанно иметь так много специфичных для Java решений по общему шаблону. Решение Пью «лучше» любого из двух других в том, что оно не требует ни синхронизации, ни жертвования ленивым созданием экземпляров, оно просто требует немного больше ввода для создания держателя.......
Неправильный пример Java сломан, так как он не является неправильным. Сам объект не имеет изменяемого состояния, и поэтому не страдает от проблем с потоками. Вторая проверка находится в синхронизированном блоке, поэтому нет шансов на создание более одного экземпляра (на один загрузчик классов).
Предлагаю удалить, а не исправлять неверный пример. Он будет только сбивать с толку и, вероятно, послужит шаблоном для программистов, которые не поняли сути проблемы.
Кстати, очень старые версии Java от Sun (вроде 1.0) по-видимому, неправильно собирали классы мусора. В спецификации теперь говорится, что существует неявная сильная ссылка из экземпляра class-loader на все загруженные им классы.
Извините, но это неверно. Пожалуйста, прочтите любую из сотен ссылок (одна опубликованная в этой статье) понять почему. Ваше предположение, что он не может создать несколько примеры неверны.
> Извините, но это неверно. Пожалуйста, прочитайте любой из сотен > ссылок (одна из которых размещена в этой статье), чтобы понять, почему. Это правильно, пока есть вторая проверка внутри синхронизированного метод createInstance()!
Я обсудил этот вопрос с некоторыми разработчиками из моей команды: Насколько мы понимаем проблему (прочитав блог Жеу Джорджа, а также статья Питера Хаггара из IBM, ссылка на которую там есть), причина двойной проверки блокировка на отказ *НЕ* возможна, поскольку сам механизм неисправен.
На самом деле, это, похоже, серьезная ошибка в *некоторых* JIT-компиляторах: Память выделяется и назначается ссылке *до* конструктора объект выполнен!
Значение lvalue присваивания *изменяется* до того, как правая часть будет полностью оценена! Подробную информацию можно найти в статье Питера Хаггара. Компиляторы, затронутые этой проблемой, скорее всего, вызовут гораздо более серьезные проблемы. проблемы, чем эта. Строка теряет свою неизменность (как упоминал Хаггар), будучи лишь один из очевидных. Если такое поведение действительно нарушает спецификации (а я этого делать не собираюсь) для дальнейшего изучения), то двойная проверка блокировки *работает*.
В случае, если автор двух процитированных выше строк все еще не согласен с этим, я смиренно попросит ответа - возможно, более подробного, чем обычное "неправильно! rtfm!". С наилучшими пожеланиями, Феликс Элерманн, старший разработчик, Jenomics GmbH
Вы имеете в виду пример двойной проверки блокировки, в котором говорится:
Используя предложение в антишаблоне двойной проверки блокировки , ключевое слово synchronized гарантирует, что только один поток может войти в метод createInstance() одновременно.
открытый класс Singleton { частный статический Singleton ЭКЗЕМПЛЯР = null ; // Частный конструктор подавляет частный синглтон() {} //синхронизированный создатель для защиты от проблем с многопоточностью частный синхронизированный статический void createInstance() { INSTANCE = new Singleton(); } публичный статический синглтон getInstance() { if (INSTANCE == null) createInstance(); return INSTANCE; } }
?
Я имею в виду, что этот код неверный , верно? Если два потока одновременно обращаются к "getInstance()" (и getInstance вообще не вызывался), и они оба обращаются одновременно к if(INSTANCE == null), они оба скажут "да, это null".
Теперь один из них войдет в "createInstance", а другой будет просто заблокирован перед createInstance (но после "if (INSTANCE == null)"). Итак, первый войдет, создаст новый экземпляр Singleton, вернется из createInstance и вернет этот экземпляр, а другой поток вызовет (снова) createInstance, и он вернет другой экземпляр.
Добавление еще одного «if(INSTANCE == null)» внутри «createInstance» положит конец этому неправильному поведению.
Нет, не будет. Пожалуйста, прочтите любую из сотен ссылок (одна опубликованная в этой статье), чтобы понять, почему.
И, отвечая на первый комментарий в этой теме, удаление условия «if(INSTANCE == null)» в getInstance или добавление «synchronized» в getInstance сработало бы, но если бы много потоков обращались к «getInstance», они все бы блокировали друг друга без необходимости (я думаю).
Это НЕ проверено дважды:
открытый класс Singleton { частный статический Singleton ЭКЗЕМПЛЯР = null ; // Частный конструктор подавляет частный синглтон() {} //синхронизированный создатель для защиты от проблем с многопоточностью частный синхронизированный статический void createInstance() { INSTANCE = new Singleton(); } публичный статический синглтон getInstance() { if (INSTANCE == null) createInstance(); return INSTANCE; } }
«Двойная проверка» требует «проверить, если null», затем «синхронизировать», затем «Проверить, если null, снова», затем «создать». Вот пример двойной проверки:
открытый класс Singleton { частный статический Singleton ЭКЗЕМПЛЯР = null ; // Частный конструктор подавляет частный синглтон() {} //синхронизированный создатель для защиты от проблем с многопоточностью частный синхронизированный статический void createInstance() { если (INSTANCE == null) ЭКЗЕМПЛЯР = новый Синглтон(); } публичный статический синглтон getInstance() { if (INSTANCE == null) createInstance(); return INSTANCE; } }
открытый класс Singleton { частный статический класс Holder { частный статический финальный Singleton ЭКЗЕМПЛЯР = новый Singleton(); } // Частный конструктор подавляет частный синглтон() {} публичный статический синглтон getInstance() { возврат Держателя.ЭКЗЕМПЛЯРА; } } — Предыдущий неподписанный комментарий добавлен 83.231.170.196 (обсуждение) 12:18, 16 июля 2019 (UTC)
Хотя идиома Double Checked Locking (DCL) хорошо работает в средах с четко определенным барьером памяти, DCL обычно считается антишаблоном в Java, поскольку модель памяти Java не имела такового до JDK 5. Хотя, как отмечали другие, DCL можно модифицировать для корректной работы в средах JDK 5 (или выше), лучшим выбором для реализации Java Singleton является идиома Initilization On Demand Holder. Идиома Initialization On Demand Holder работает во всех средах Java, она хорошо работает и является простым шаблоном для реализации. Подробности см. на следующих страницах:
http://www.cs.wustl.edu/~schmidt/PDF/DC-Locking.pdf http://www.cs.umd.edu/~pugh/java/memoryModel/ http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
Чтобы помочь статье Singleton сосредоточиться на том, что такое Singleton и как его можно правильно реализовать, я рекомендую удалить обсуждение того, как Singleton может быть неправильно реализован в Java. Можно включить ссылку на Double Checked Locking Idiom, возможно, как нейтральную ссылку или как ссылку на анти-шаблон, а статья DCL могла бы обсудить или дать ссылку на анти-шаблон DCL, распространенный в Java.
Ян Нильсен 18:12, 2 июля 2006 (UTC)
Я добавил новую статью под названием Initialization on Demand Holder Idiom , которая описывает идиому и приводит пример на Java. Если нет возражений, я изменю эту статью, удалив обсуждение того, как не следует реализовывать синглтон, чтобы сосредоточить ее на том, что такое синглтон и как правильно его реализовать. Если есть возражения, пожалуйста, разместите их здесь как можно скорее.
Ян Нильсен 16:07, 5 июля 2006 (UTC)
Я удалил «неправильную реализацию Java».
Ян Нильсен 05:51, 13 ноября 2006 г. (UTC)
Раздел: «C# (использование IDisposable)»
Метод: private void dispose(bool disposing)
private void dispose ( bool disposing ) { // Проверьте, был ли уже вызван Dispose. if ( ! this . disposed ) { // Если disposing равен true, удалите все управляемые // и неуправляемые ресурсы. if ( disposing ) { if ( someStream != null ) { someStream . Close (); } } // Здесь вызовите соответствующие методы для очистки // неуправляемых ресурсов. // Если disposing равен false, // выполняется только следующий код. if ( someComObject != null ) { Marshal . ReleaseComObject ( someComObject ); } } disposed = true ; }
Комментарий "Вызовите соответствующие методы для очистки неуправляемых ресурсов здесь. Если disposing ложно
Код «if (someComObject != null)» будет выполнен ВСЕГДА, поскольку код не соответствует if выше.
В примере потокобезопасного C++ единственный экземпляр выделяется с помощью new. Я не вижу, как деструктор может быть вызван. Разве пример не должен использовать auto_ptr или эквивалент? — Предыдущий JohnCFemiani 15:45, 7 декабря 2006 (UTC)
auto_ptr
примеру: деструктор для Singleton
не обязательно должен существовать, но если он существует, он должен быть публичным. В противном случае auto_ptr
мы не сможем его увидеть. По крайней мере, таково мое (ограниченное) понимание. -- Quuxplusone 23:26, 6 декабря 2006 (UTC) Лично я считаю, что полезно увидеть конкретные примеры этих вещей на языке, который я понимаю; возможно, их можно отнести к разделу «приложения» или чему-то подобному. Также кажется, что ни один из примеров C++ на самом деле не контролирует аспект «максимум» синглтона. Поскольку конструкторы не являются закрытыми, можно создать столько экземпляров, сколько захочется. Шаблонное решение, используемое здесь, не может скрыть конструкторы; оно может только гарантировать, что есть по крайней мере один глобальный экземпляр. JohnCFemiani 15:45, 7 декабря 2006 (UTC)
Текущая "потокобезопасная" версия на самом деле не является потокобезопасной. Поскольку первая проверка указателя не защищена мьютексом, возможно (на платформах, где доступ к указателю не атомарный), что во время теста указатель будет наполовину записан, и, следовательно, не будет ни нулевым (что приведет к тому, что then-оператор не будет выполнен), ни допустимым (что заставит возвращаемый указатель указывать на какое-то неправильное место). Тогда текущий поток может использовать указатель до того, как другой поток, который начал записывать указатель, получит возможность закончить запись указателя (и, следовательно, снова сделать его допустимым).
Более того, он также не соответствует стандарту, потому что в точке, где auto_ptr инициализируется, класс Singleton все еще неполный, и согласно стандарту C++ инстанцирование шаблона стандартной библиотеки на неполном типе приводит к неопределенному поведению. В частности, в случае auto_ptr, если деструктор auto_ptr инстанцируется до завершения класса Singleton, это приведет к удалению на неполном типе, что не определено, если Singleton имеет нетривиальный деструктор (т. е. либо есть явно написанный деструктор, либо у него есть какой-либо базовый класс или переменная-член с нетривиальным деструктором, например, если какой-либо член является std::string, стандартным контейнером или умным указателем). -- 132.199.97.100 ( talk ) 14:28, 1 июля 2008 (UTC)
Непотокобезопасный пример C++, использующий Curiously Recurring Template Pattern, говорит, что он также известен как "Meyers Singleton" (в честь Скотта Мейерса , который, по-видимому, дал реализацию в More Effective C++ ). Однако это обсуждение на веб-форуме — единственный онлайн-источник, который я смог найти для "Meyers Singleton" (кроме собственного веб-сайта Мейерса, на котором говорится, что он его изобрел, но не говорится, что это такое). И это обсуждение на форуме не согласуется с нашей статьей — их "Meyers Singleton" вообще не использует CRTP! Поэтому я восстановил маркер {{ citation needed }} к этому заявлению, и если его не удастся проверить в течение нескольких недель, я просто буду WP:BOLD и удалю материал "Meyers". Кто-нибудь, у кого есть More Effective C++, может поискать его. -- Quuxplusone 04:09, 9 января 2007 (UTC)
Я изменил диаграмму классов, потому что подчеркивания (они означают «статичный») несут очень важную информацию для этого шаблона. -- Trashtoy 03:04, 24 февраля 2007 (UTC)
Также в PHP можно использовать статическую переменную функции вместо статической переменной класса, что, на мой взгляд, делает код более аккуратным:
класс Синглтон { публичная статическая функция getInstance() { статический $экземпляр; если (!$instance) $instance = новый я; вернуть $экземпляр; } private function __construct() { /* Невозможно создать больше */ } private function __clone() { /* Невозможно клонировать */ } // Остальная часть реализации класса находится здесь... }$instance = Singleton::getInstance();
К сожалению, в настоящее время нет простого способа поместить это в класс, который можно расширить! (См. комментарии http://www.php.net/singleton, где есть некоторые идеи, как обойти это ограничение.)
-- DaveJamesMiller 18:12, 4 марта 2007 (UTC)
В книге Gang-Of-Four шаблон singleton характеризуется двумя требованиями: во-первых, ограничение инстанцирования класса одним экземпляром, и, во-вторых, предоставление глобальной точки доступа к этому экземпляру. В статье в настоящее время описывается первое требование, но (насколько я могу судить) не описывается явно второе требование. Это намеренно? — Тобиас Бергеманн 13:14, 5 марта 2007 (UTC)
"Выполнение
Шаблон «синглтон» реализуется путем создания класса с методом, который создает новый язык с возможностями параллельной обработки. Метод должен быть сконструирован для выполнения как взаимоисключающая операция.
Вышеизложенное, похоже, не имеет никакого смысла. Я вижу старую правку, которая заставляет эту ерунду меняться - предыдущий текст, похоже, имеет смысл. Может ли кто-то, кто знает, как вернуть его обратно - если предыдущий текст действителен?
Дэвидмаксуотерман 04:01, 9 августа 2007 г. (UTC)
Пожалуйста, добавьте больше встроенных ссылок. Shinobu 04:40, 2 сентября 2007 (UTC)
Я бы предложил, чтобы эта запись содержала ссылку на тот факт, что синглтон почти повсеместно считается плохим проектом.
Некоторые минимальные ссылки для этой точки зрения:
Почему синглтоны — это зло
Одиночка считается глупым
Используйте свои синглтоны с умом — Предыдущий неподписанный комментарий добавлен Swiftcoder ( обсуждение • вклад ) 20:48, 11 ноября 2007 (UTC)
Почему пример создает мьютексы вместо использования pthread_once? Bpringlemeir ( обсуждение ) 20:12, 14 января 2008 (UTC)
Следует добавить что-то относительно необходимости перезаписи метода clone в java — Предыдущий неподписанный комментарий добавлен Lax4mike ( обсуждение • вклад ) 16:14, 21 января 2008 (UTC)
почему статический volatile используется для singleton и почему не только статический в примере java 1.5? Какая разница? — Предыдущий комментарий unsigned добавлен 210.210.79.19 ( talk ) 07:10, 19 февраля 2008 (UTC)
This topic is in need of attention from an expert on the subject. The section or sections that need attention may be noted in a message below. |
Предупреждение: несмотря на утверждение «Это единственная действительно потокобезопасная реализация ленивой инициализации синглтона» ниже, использование std::auto_ptr означает, что разрушение не будет правильно синхронизировано с созданием, и, следовательно, это исключает. RFST ( обсуждение ) 14:47, 9 мая 2010 (UTC)
Этот пример не должен оставаться в Википедии, потому что он неверен. В C++ из-за стандарта языка компилятору разрешено переупорядочивать выполнение, пока выполнение согласовано в контрольной точке (например, в конце блока выполнения). Здесь компилятор обычно переупорядочивает конструкцию объекта и присваивает синглтон возвращаемому значению до того, как объект синглтона будет полностью построен. Поток, который хочет получить синглтон в то же время, поэтому вернет значение объекта, который еще не был полностью построен. И из-за оптимизации нет способа защититься от этого переносимым образом.
Если вы хотите узнать об этом больше, прочитайте эту замечательную статью Скотта Мейера и Андрея Александреску.
Единственное потокобезопасное и достаточно переносимое (в системах POSIX) решение для синглтона с ленивой инициализацией — это следующее, БЕЗ DCL:
#include <pthread.h> #include <память> #include <iostream> // Пропущены объявления Mutex и MutexLockerкласс Singleton { public : static Singleton & Instance (); int example_data ; ~ Singleton () { } защищено : Singleton () : example_data ( 42 ) { } private : static std :: auto_ptr < Singleton > theSingleInstance ; static Mutex m ; }; Singleton & Singleton::Instance () { // Обратите внимание на отсутствие первого `if', в противном случае это привело бы к ошибке. MutexLocker receive_lock ( m ); if ( theSingleInstance . get () == 0 ) { theSingleInstance . reset ( new Singleton ); } return * theSingleInstance ; } std :: auto_ptr < Singleton > Singleton :: theSingleInstance ; Мьютекс Singleton :: m ; int main () { std :: cout << Singleton :: Instance (). example_data << std :: endl ; return 0 ; }
Вы заметите, что каждый раз, когда вы обращаетесь к Singleton::Instance(), это очень затратно из-за блокировки. Поэтому ваши потоки должны использовать локальное кэширование хранилища следующим образом:
#include <singleton.h> class Thread { // Цикл потока void run () { // раннее получение и кэширование экземпляра singleton Singleton * singleton = Singleton :: Instance (); // остальная часть кода // с этого момента используйте переменную singleton action1 ( * singleton ); action2 ( * singleton ); // и т. д. } };
Это единственная действительно потокобезопасная реализация ленивой инициализации синглтона. Другой способ, конечно, не использовать ленивую инициализацию и создать синглтон в общей области действия вашей программы, до main. Это даже устраняет использование блокировок. Но если создание вашего синглтона требует больших затрат или он зависит от какого-либо ресурса, это единственный потокобезопасный способ. Вышеуказанное вполне переносимо (по крайней мере, в системах POSIX) и должно быть предоставлено вместо текущего примера. - Sylvain 192.54.144.229 ( talk ) 06:46, 30 апреля 2008 (UTC)
Существует множество тонких проблем с реализацией синглтона в C++. В этой статье делается попытка рассмотреть некоторые из них, и при этом делается много ошибочных утверждений. Если статья должна сохранить свой текущий уровень технического описания, следует принять во внимание:
1- Фиаско статического порядка инициализации. Вам не гарантирован порядок построения глобальных переменных, членов пространства имен и статических членов класса между единицами трансляции.
2. Статический порядок деинициализации терпит фиаско. (Таким образом, указание пользователю кэшировать копию возвращаемого указателя также проблематично.) Если вы начнете уничтожать статические объекты хранения, вы рискуете, что статический объект хранения A будет ссылаться на другой объект B в своем деструкторе после того, как B будет уничтожен.
3- Двойная проверка блокировки нарушается по многим причинам. Причины обычно сводятся к 3a- переупорядочиванию компилятора 3b- переупорядочиванию оборудования и другим причинам одноядерного процессора 3c- проблема согласованности кэша для многоядерных процессоров
4- Я заметил, что в примере pthread мьютекс объявлен как статический член класса.
Если мы объявим его как глобальный, член пространства имен или статический член класса, то получим фиаско статического порядка инициализации. Мьютекс может не быть создан до его первого использования.
Мы также должны быть обеспокоены параллельной инициализацией мьютекса. Мы должны создать мьютекс в однопоточной среде. Не существует способа потокобезопасного создания мьютекса при первом использовании (кроме использования другого мьютекса).
Решение состоит в том, чтобы сделать экземпляр мьютекса здесь его собственным синглтоном, синглтоном с активной инициализацией конструкции при первом использовании. Например, что-то вроде:
/*предполагая, что мы можем допустить утечку мьютексов. Если нет, то нам придется беспокоиться о статическом фиаско порядка деинициализации.*/ namespace { Mutex& getMutex() { static Mutex * m = new Mutex; return *m; } }
/*простая потокобезопасная версия*/ T& getSingleton() { Guard g(getMutex()); static T* t = new T; return *t; }
/*заставить инстанцирование мьютекса произойти до вызова main, то есть, предположительно, до того, как кто-то создаст поток. Если кто-то создаст потоки до вызова main, все ставки в любом случае отменяются. НЕ делайте этого (создавайте потоки до вызова main).*/ namespace { struct ForceMutexInstantiation { ForceMutexInstantiation() {getMutex();} } instance; }
Немного более продвинутое обсуждение включало бы барьеры памяти или локальное хранилище потока как более быстрые реализации потокобезопасного синглтона с ленивой инициализацией.
-Джош — Предыдущий неподписанный комментарий добавлен 12.108.188.134 ( обсуждение ) 20:42, 23 сентября 2008 (UTC)
В этом разделе слишком много сложностей, и он не предоставляет пример без ошибок, происходит утечка памяти, и мы связываем специфичные для операционной системы вещи, такие как мьютекс, отображение памяти и т. д. Я думаю, что мы должны предоставить простой пример, который будет работать независимо от операционной системы и примитивов, предоставляемых ОС. Я постараюсь предоставить пример Singleton без утечки и удалить текущий пример, который привязан к ОС и несколько запутан. Пожалуйста, ознакомьтесь с изменениями. Спасибо. — Nvineeth talk 17:54, 14 октября 2008 (UTC)
Это тот парень, который внес огромные изменения, которые вы стерли.
Я добавил отказ от ответственности, что код неверен в том виде, в котором он написан сейчас. Я согласен, что он был слишком сложным, и меня устраивает этот пример псевдокода. Однако давайте не будем стесняться в выражениях. Образец до вашего изменения был достаточно платформенно-независимым и правильным. Ваш образец, хотя и хорош как образец для иллюстрации точки зрения, имеет много проблем. Я перечислил проблемы, которые вы затем стерли.
Ваш пример не будет работать правильно при объединении в реальную программу из-за: 1. Неполадки в порядке статической инициализации. 2. Неполадки в порядке статической деинициализации. 3. Отложенной динамической инициализации объектов области пространства имен. 4. Запись в std::cout не защищена мьютексом.
Например, общепринятым решением для фиаско статического порядка деинициализации является утечка синглтона. Это совершенно нормально, и предпочтительный подход для любой операционной системы — вернуть ресурсы синглтона, когда процесс умирает (читай: все операционные системы для таких ресурсов, как память, дескрипторы файлов и т. д.). В большинстве случаев утечка синглтона НЕ является ошибкой.
Предлагаю прочитать эти ссылки, чтобы освежить в памяти основные проблемы реализации потокобезопасного синглтона с ленивой инициализацией. (Это ссылки, которые вы ранее удалили с главной страницы.) http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
В первой ссылке описывается, как самое простое и лучшее решение — просто слить синглтон. Обратите внимание, что первая ссылка — это самое близкое, что есть к официальному FAQ по C++; она поддерживается людьми, связанными с комитетом по стандарту C++.
Более того, зачем удалять класс защиты мьютекса "Lock"? Общепринятый стиль C++ — RAII, http://en.wikipedia.org/wiki/RAII. —Предыдущий комментарий без знака добавлен 12.108.188.134 ( обсуждение ) 00:30, 23 октября 2008 (UTC)
Вся цель синглтона — обеспечить безопасную, единую конструкцию экземпляра типа. Пример — это хорошо сформированная и хорошо определенная программа. Однако, если этот код включить дословно в другую программу, он не будет работать. Это одна из целей примера: публикация правильного фрагмента кода, который можно использовать в более крупном проекте, критерий, по которому этот пример не проходит.
1. Ошибка статического порядка инициализации является одной из основных проблем правильной реализации синглтона, и поэтому правильный пример должен включать код для решения этой проблемы.
2- Фиаско статической деинициализации - еще одна серьезная проблема в правильной реализации синглтона для любой реальной программы. Принятый ответ - позволить ему утекать, если вам не нужен вызываемый деструктор, что справедливо для большинства синглтонов, которые я видел.
3- Анальная проблема - задержка динамической инициализации объектов области пространства имен. Большинство компиляторов не задерживают ее, но есть один, который оптимизирует неиспользуемые единицы трансляции. (Я не уверен, как именно он генерирует правильные DLL в этом случае, но мне сказали надежные люди, что это так.) Таким образом, технически единственный гарантированный способ сделать синглтон - это pthread_once или что-то подобное, а не код, который вы опубликовали.
4- Печать в cout без защиты мьютекса также может привести к сбою в некоторых системах.
5- Кроме того, вы не понимаете RAII. Вот пример подхода "в стиле C":
{ mutexInstance.acquire(); //делаем то, что должно быть атомарным mutexInstance.release(); }
Вот тот же код с RAII:
{ MutexGuard охранник(mutexInstance); //делаем то, что должно быть атомарным }
RAII предпочтительнее в C++ по следующим причинам: 1. Локальность проблем. Получение и освобождение некоторого ресурса являются аспектами одной и той же проблемы. Таким образом, они должны быть как можно ближе друг к другу. Это снижает вероятность ошибок программиста. При подходе в стиле «C» у вас может быть ранний возврат или что-то еще, когда вы забудете поместить mutexInstance.release(), что, вероятно, приведет к взаимоблокировке вашей программы. 2. Подход в стиле RAII безопасен для исключений. «Стиль C» не снимет блокировку, если исключение будет выброшено без явного try + catch. (Это всего лишь краткий обзор. Почитайте об этом у людей, которые могут объяснить это лучше меня.)
Кроме того, RAII на самом деле не приводит к "созданию и уничтожению дополнительных объектов" как таковому. Любой оптимизирующий компилятор сгенерирует тот же двоичный файл для "стиля C" и способа RAII (за исключением того, что двоичный файл RAII будет иметь немного дополнительного кода, который также делает его безопасным для исключений. Для всех коммерческих компиляторов это не добавит никакого кода на обычный путь выполнения, что в результате приведет к отсутствию накладных расходов по сравнению с подходом "стиля C".) (Кроме того, почитайте об исключениях в C++. Обработка исключений в C++ не бесплатна на некоторых платформах, но даже если это одна из плохих платформ, введение еще одного стекового объекта не повлияет на производительность.)
- Тот же парень, он же Джош, профессиональный программист на C++ и Java. — Предыдущий неподписанный комментарий добавлен 12.108.188.134 ( обсуждение ) 16:31, 23 октября 2008 (UTC)
Вы упускаете суть. Эта реализация использует статический объект хранения, поэтому если другой объект попытается использовать этот интерфейс во время статической инициализации, есть вероятность 50 на 50, что программа завершится сбоем. Альтернативой является «Вам разрешено использовать этот интерфейс, синглтон, только после вызова main и только до выхода из вызова main», то есть не во время статической инициализации и статической деинициализации.
Что касается того, применимо ли это, это зависит от обстоятельств. Вам нужен правильный пример или просто неправильный пример псевдокода, иллюстрирующий идею? Если это не так, то, по крайней мере, должно быть предупреждение: «Это неправильный код, и он не будет работать, если его объединить с реальной кодовой базой. (Возможно, с перечислением X причин, по которым это так.) Этот пример здесь только для иллюстрации концепции синглтона». --Джош —Предыдущий неподписанный комментарий добавлен 12.108.188.134 ( обсуждение ) 19:33, 24 октября 2008 (UTC)
Я удалил это из раздела «Общее использование»:
потому что это ни в коем случае не является Общим использованием или любым видом использования. Если уж на то пошло, то и пункт выше не является таковым - но по крайней мере пункт выше имеет смысл для читателя. Этот пункт не имеет смысла: например, ссылка на "виртуальную машину", учитывая, что нет причин ожидать, что БЫТЬ обязательно виртуальная машина. Я также не убежден, что высказанный здесь пункт достаточно важен или неочевиден, чтобы заслуживать быть в статье, поэтому я не приложил усилий, чтобы найти где-то еще в статье, чтобы поместить его. Educres ( talk ) 11:52, 29 октября 2008 (UTC)
Кто-то изменил конструктор на protected, вместо этого должно быть private.
Во втором классе Singleton Пью переопределение clone() кажется мне излишним. Пример Singleton получен из Object, который уже выполняет работу по выбрасыванию CloneNotSupportedException. Это было бы необходимо только в том случае, если Singleton implements Cloneable
, но тогда обычно вы не выбрасываете CloneNotSupportedException. С другой стороны, если вы выводите класс, который поддерживает clone(), попытка выбросить CloneNotSupportedException приводит к ошибке времени компиляции. Если я не прав, пожалуйста, предоставьте хотя бы ссылку, где люди могут узнать причину такого подхода. —Предыдущий неподписанный комментарий добавлен 194.113.148.55 (обсуждение) 12:48, 27 апреля 2009 (UTC)
Привет, давний пользователь, но впервые участвую здесь. Я думаю, что пример кода Obj-C следует изменить в соответствии с рекомендациями Apple в их документации.
Они описали свою интерпретацию здесь: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaDesignPatterns/CocoaDesignPatterns.html#//apple_ref/doc/uid/TP40002974-CH6-SW66
А вот пример кода: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32
Однако я не осмеливаюсь редактировать запись внезапно, так как не хочу наступать кому-то на ногу. —Предыдущий неподписанный комментарий добавлен 130.225.195.131 (обсуждение) 08:20, 24 марта 2009 (UTC)
Может кто-нибудь что-нибудь добавить о том, как уничтожить объект singleton? Как/почему и т. д. и т. п.?? Обсуждение по этому поводу есть на "http://sourcemaking.com/design_patterns/to_kill_a_singleton"
Raoravikiran ( обсуждение ) 09:03, 19 мая 2009 (UTC)
Не слишком ли следующая фраза ориентирована на определенный язык? "Еще одно заметное отличие заключается в том, что статические классы-члены не могут реализовать интерфейс, если только этот интерфейс не является просто маркером. Поэтому, если класс должен реализовать контракт, выраженный интерфейсом, он действительно должен быть синглтоном". Я думаю, он говорит о JAVA, потому что статические члены в некоторых языках, таких как C++, могут наследовать класс, созданный только из чистого виртуального метода (который является интерфейсом), и реализовывать их через определение своего класса.
Я думаю, что было бы яснее убрать эту часть пояснения по реализации. Что вы об этом думаете?
daminetreg 82.126.199.131 (обсуждение) 14:59, 16 ноября 2009 (UTC)
Стоит упомянуть, что Flash Actionscript на самом деле не поддерживает истинные синглтоны (потому что конструктор любого класса всегда должен быть публичным). Однако существует множество способов эмулировать это, и пример на этой странице — всего лишь пример ОДНОГО способа. Это, конечно, не ОПРЕДЕЛЕННЫЙ способ сделать это (поскольку его нет). -- 61.194.119.130 (обсуждение) 07:45, 7 декабря 2009 (UTC)
Стоит ли упоминать Javascript в разделе прототипов? Многие дети Javascript не понимают природу языка и могут прийти сюда, чтобы посмотреть. Sj26 ( talk ) 06:50, 1 февраля 2010 (UTC)
В настоящее время это игрушечные примеры, то, что вы изучаете один раз в классе, чтобы узнать его проблемы и никогда больше этого не делать. В последний раз, когда я это исправлял, кто-то сказал, что это слишком сложно для записи в энциклопедии, но я бы хотел, чтобы хотя бы были опубликованы предостережения.
Примером, не являющимся потокобезопасным, является синглтон Мейера, хотя большинство компетентных людей посоветуют вам не использовать его из-за фиаско с порядком статической деинициализации. http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14 Предпочтительно заменить его на указатель и, возможно, добавить примечание, обобщающее приведенную выше ссылку.
Первый потокобезопасный синглтон — это что угодно, но не так. Он настолько плох, что я его удаляю.
1- Для синглтона есть две переменные, локальная статическая функция и статический член класса. Я не уверен, почему код так запутан.
2- Затем есть пустая операция "if (true)" без видимой причины, возможно, результат ленивого редактирования, того самого редактирования, которое предупреждало о двойной проверке блокировки. Там также есть другие части, которые выглядят так, будто это была быстрая правка, чтобы исправить (сломанный) пример двойной проверки блокировки.
3- Затем у нас есть посторонняя функция createInstance. Код должен быть просто в функции getInstance. Нет смысла иметь еще одну функцию. Просто больше запутывания. Особенно плохо, что функция createInstance является публичной, легкодоступной и не документирована "не используйте!!".
4. В примере также наблюдается фиаско статического порядка деинициализации, и его также следует исправить.
5- Он также страдает от фиаско статического порядка инициализации в объекте Mutex области пространства имен. Это нормально для систем POSIX, где вы можете создать мьютекс до инициализации во время выполнения с помощью инициализатора const expr, но win32 требует инициализации во время выполнения предоставленных им мьютексов. Это очень сложная и, как правило, плохо понимаемая тема. Я предлагаю прочитать http://groups.google.com/group/comp.lang.c++/browse_thread/thread/dd7f3af258a800cd/ded6c1299bbf7776?#ded6c1299bbf7776 для хорошего обсуждения этой темы. —Предыдущий неподписанный комментарий добавлен 12.108.188.134 ( обсуждение ) 23:36, 12 марта 2010 (UTC)
Второй потокобезопасный синглтон лучше, но все еще имеет следующие проблемы:
1- фиаско статического порядка инициализации, которое в комментариях отвергается как "немного надуманное". Я усиливаю предупреждение, так как это реальная проблема. Также обратите внимание, что конструкция синглтона не является потокобезопасной, если к ней обращаются несколько потоков из-за проблемы статического порядка инициализации.
2- Все еще имеются проблемы со статическим порядком деинициализации.
Это тот же парень, которому RFST "ответил". Когда я читаю запись в энциклопедии, которая претендует на звание "Пример шаблона синглтон, реализованного в C++", я ожидаю, что это будет правильная реализация шаблона синглтон в C++, а не неправильная реализация или реализация в псевдокоде. Я скоро займусь исправлением раздела C++.
Примеру Delphi нужна проверка Assigned в общем деструкторе. Если экземпляр освобождается во время выполнения, то при отсутствии проверки возникнет ошибка. Тот, кто ее удалил, пожалуйста, воздержитесь от повторного использования. —Предыдущий неподписанный комментарий добавлен Freddy1990 ( talk • contribs ) 10:25, 3 апреля 2010 (UTC)
Текущая версия характеризует шаблон синглтона как средство «реализации математической концепции» синглтона. Я не буду ее менять, но, возможно, кто-то более осведомленный, чем я, должен это сделать? Набор синглтонов — это чрезвычайно общая концепция, и мне не ясно, что этот шаблон проектирования «реализует» это понятие больше, чем, скажем, сумка с одним яблоком. Очевидно, что между этими двумя понятиями есть связь, и есть веская причина, по которой здесь используется термин «синглтон», но мне кажется, что теоретико-множественные концепции просто полезны для описания шаблона проектирования, а не чего-то, что шаблон призван моделировать. Так что, как я уже сказал, возможно, кто-то мог бы либо прояснить текст соответствующим образом? (То есть, либо так, чтобы он устранил мою обеспокоенность, либо, если я что-то неправильно понимаю, так, чтобы это недопонимание в будущем было исключено.) Спасибо. MJM74 ( обсуждение ) 06:34, 28 июля 2010 (UTC)
Я действительно сбит с толку тем, как эта статья стала такой раздутой и сложной. Хотя я согласен, что следует предоставить как можно больше подробностей о происхождении, концепции и реализации шаблона Singleton, я считаю, что информацию о конкретных примерах с использованием шаблона AbstractFactory и реализациях на нескольких языках следует удалить. В Википедии не должно быть 100 примеров для каждого языка для этого простого шаблона (если бы это было так, эта страница была бы еще более нечитаемой, чем сейчас). Я говорю «простой», потому что это первый шаблон, который я когда-либо изучал на занятиях (и я подозреваю, что он распространен и для всех остальных).
Если бы это зависело от меня, эта страница состояла бы из 4-5 абзацев, примера кода на строго типизированном, распространенном языке программирования (например, Java) и диаграммы классов, которая доступна в настоящее время. Если люди жалуются, что нет 100 примеров для каждого языка, им следует научиться программировать на этом языке, прежде чем пытаться использовать в нем шаблоны.
Если никто не будет против, я, возможно, перенесу примеры на другую страницу для ясности к концу недели. — Предыдущий неподписанный комментарий добавлен Fuzzyrichie (обсуждение • вклад ) 04:34, 15 декабря 2010 (UTC)
забудьте мой комментарий - он неадекватен.
Давайте рассмотрим два примера синглтона. На моем 8-битном Amstrad CPC, работающем под управлением Symbos (потрясающая многопрограммная ОС, использующая незащищенное пространство памяти Z80), я пишу приложение на C++, в котором есть синглтон. Но... это не синглтон с точки зрения «системы», у него есть 2 экземпляра, когда я запускаю 2 экземпляра приложения C. Допустим, синглтон был изменен, чтобы стать настоящим синглтоном (по всей системе), тогда есть законные применения этого, например, создание экземпляра графической подсистемы в приложении C и отсутствие двух подсистем, которые выходят из строя из-за запуска нескольких копий одной и той же программы. Теперь я знаю, что это два примера, но в статье говорится системный или общесистемный несколько раз. Разве определение синглтона не должно означать один экземпляр в определенной области? или что-то в этом роде?
ZhuLien 116.240.194.132 (обсуждение) 05:39, 4 апреля 2014 г. (UTC)
В комментарии под разделом «Пример» говорится следующее:
Однако каждый пример в этом разделе посвящен тонкостям реализации шаблона singleton в Java. Нам нужно два примера, и только два: один для ленивой инициализации и один для жадной инициализации. Все остальное в этой статье — хлам, специфичный для Java. Я сам произведу очистку, если никто не возражает или не доберется до меня. Hpesoj00 ( talk ) 15:51, 27 июля 2016 (UTC)
synchronized
) – это хороший и лаконичный пример шаблона Singleton в Java, поэтому я его сохранил.final
слова, оба специфичные для Java особенности. Третье преимущество упоминает, что нет необходимости синхронизировать вызовы getInstance
, которые я удалил для улучшения потока, поскольку проблема блокировки теперь обсуждается в разделе «Ленивая инициализация», где она требуется. Не стесняйтесь добавлять это преимущество обратно в текст, если вы считаете это необходимым.Здравствуйте, уважаемые википедисты!
Я только что изменил одну внешнюю ссылку на шаблон Singleton . Пожалуйста, уделите немного времени, чтобы просмотреть мои правки . Если у вас есть какие-либо вопросы или вам нужно, чтобы бот игнорировал ссылки или страницу в целом, посетите этот простой раздел FaQ для получения дополнительной информации. Я внес следующие изменения:
Закончив просмотр моих изменений, вы можете следовать инструкциям в шаблоне ниже, чтобы исправить любые проблемы с URL-адресами.
Это сообщение было опубликовано до февраля 2018 года . После февраля 2018 года разделы страниц обсуждения "Внешние ссылки изменены" больше не генерируются и не отслеживаются InternetArchiveBot . Никаких специальных действий в отношении этих уведомлений страниц обсуждения не требуется, кроме регулярной проверки с использованием инструкций инструмента архивации ниже. Редакторы имеют право удалять эти разделы страниц обсуждения "Внешние ссылки изменены", если они хотят очистить страницы обсуждения от загромождения, но перед выполнением массовых систематических удалений ознакомьтесь с RfC . Это сообщение динамически обновляется через шаблон (последнее обновление: 5 июня 2024 г.) .{{source check}}
Привет.— InternetArchiveBot ( Сообщить об ошибке ) 06:47, 6 декабря 2017 (UTC)
Всем привет,
Эту статью нужно немного переработать. Существующий пример Java для ленивой инициализации можно найти в статье "Double-checked locking", а также гораздо лучший пример, поскольку текущий пример здесь можно считать анти-шаблоном. — Предыдущий неподписанный комментарий добавлен 83.231.170.196 (обсуждение) 13:44, 17 июля 2019 (UTC)
Раздел «Критика» полон неточной информации и основан только на чьем-то мнении. Такие фразы, как «потому что к синглтону можно получить доступ откуда угодно и откуда угодно без необходимости использовать какие-либо параметры» и «синглтоны трудно тестировать, потому что они несут глобальное состояние на протяжении всей программы». настолько неверны, что я даже не знаю, с чего начать. Здесь есть смесь разных концепций и, вероятно, некоторые языковые/собственные предубеждения. В этот раздел следует добавить отметку, чтобы предупредить об этом. — Предыдущий неподписанный комментарий добавлен 82.131.59.214 (обсуждение) 17:06, 2 ноября 2021 (UTC)