что сразу бросается в глаза — если у тебя на этом куске повиснет поток с высоким приоритетом, а синглтон будет создаваться потоком с низким — будут страшные тормоза.
Re[2]: Новый тип синглтона! Зацените!
От:
Аноним
Дата:
15.01.09 12:36
Оценка:
Цитата:
"что сразу бросается в глаза — если у тебя на этом куске повиснет поток с высоким приоритетом, а синглтон будет создаваться потоком с низким — будут страшные тормоза."
Спасибо!
Если есть у вас какиенить идеи как это можно лучше сделать/обойти — буду благодарен.
MT_SAFE_STATIC( ClassName, instance_name, (initializers) );
или
MT_SAFE_STATIC( ClassName, instance_name, ; );
если конструктор не принимает параметров
Кстати, VS2005 и выше это делает сама.
Re[2]: Новый тип синглтона! Зацените!
От:
Аноним
Дата:
15.01.09 13:12
Оценка:
Здравствуйте, Sergej Pupykin, Вы писали:
SP>1. к status надо всегда обращаться interlocked функциями, иначе получишь SP>проблемы на SMP.
Ну... в том моменте кода где ньюкается — доступ к status защищён interlocked функцией и я так понимаю проблемы должны возникнуть только если начнёшь чего-то удалять (Так ли это? как вы считаете?).
Но тот участок кода где удаляется я намеренно никак не защищал, чтобы не вводить разработчиков в заблуждение что удаление может быть в принципе потоковобезопасным. Понятно дело, что если во время удаления другой поток возвратит указатель, то весь код который этот указатель использует — бажный. Поэтому следует позаботится чтобы все потоки которые используют шаблон заглохли ещё до удаления экземпляра класса через delete.
SP>2. busywait циклы кривы и неэфективны.
Аноним 830 wrote: > Здравствуйте, Sergej Pupykin, Вы писали: > > SP>1. к status надо всегда обращаться interlocked функциями, иначе получишь > SP>проблемы на SMP. > > Ну... в том моменте кода где ньюкается — доступ к status защищён interlocked функцией и я так понимаю проблемы должны возникнуть только если начнёшь чего-то удалять (Так ли это? как вы считаете?).
Здравствуйте, Аноним, Вы писали:
А>Товарищи разработчики! А>Зацените! Новое моё изобретение!!! А>Потоковобезопасный шаблон "Ленивого" синглтона. А>Если будет что возразить — пишите!
Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.
Здравствуйте, Аноним, Вы писали:
А>Цитата: А>"что сразу бросается в глаза — если у тебя на этом куске повиснет поток с высоким приоритетом, а синглтон будет создаваться потоком с низким — будут страшные тормоза."
А>Спасибо! А>Если есть у вас какиенить идеи как это можно лучше сделать/обойти — буду благодарен.
Есть DCSI — double-checked serialized initialization, он же DCL (double-check locking) и DCI (double-check initialization).
Это когда, пока один инициализирует объект, остальные ждут на мьютексе.
Есть DCSI-TSD — DCSI with thread-specific data
То же, но с копией указателя на объект в TSD.
Есть DCCI (double-checked concurrent initialization).
Это когда инициализация объекта идемпотентная. Т.е. все потоки, которые увидели неинициализированный объект, начинают его создавать. Далее кто-то один атомарно подменяет глобальный указатель на свой, остальные рушат свои объекты.
Здравствуйте, remark, Вы писали:
R>Есть DCCI (double-checked concurrent initialization). R>Это когда инициализация объекта идемпотентная. Т.е. все потоки, которые увидели неинициализированный объект, начинают его создавать. Далее кто-то один атомарно подменяет глобальный указатель на свой, остальные рушат свои объекты.
Так вот оказывается как один мой лисапед правильно называется
R>
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, remark, Вы писали:
R>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.
Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту. Для этого нужно использовать само значение указателя в качестве статуса, в этом случае барьер при доступе не нужен, т.к. будет иметь место "data dependency". Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора. И еще вместо Sleep(0), лучше как в реализации boost::call_once под Win32 создавать уникальный именованный мьютекс и ждать на нем.
Единственное преимущество такого велосипеда перед DCL Implementation
в корректности работы в случае гонок при инициализации приложения, что к сожалению не такая уж и редкость. Естественно речь не идет о портируемости .
R>
Здравствуйте, Quasi, Вы писали:
Q>Здравствуйте, remark, Вы писали:
R>>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.
Q>Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту. Для этого нужно использовать само значение указателя в качестве статуса, в этом случае барьер при доступе не нужен, т.к. будет иметь место "data dependency". Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора. И еще вместо Sleep(0), лучше как в реализации boost::call_once под Win32 создавать уникальный именованный мьютекс и ждать на нем. Q>Единственное преимущество такого велосипеда перед DCL Implementation
Здравствуйте, alzt, Вы писали:
A>А для чего здесь:
A>
A>volatile static void* ptr = 0;
A>
, A>а не
A>
A>volatile static T* ptr = 0;
A>
A>?
Да дело в том что невстроенные типы статических членов функций статически инициализируются в момент первого вызова функции, а не заранее. И race conditions не возникает. void* — тип по любому встроенный. Поэтому он запишется в нуль ещё до первого вызова. А вот на счёт T* — тут я честно говоря не уверен. С одной стороны — указатель он и в африке указатель — изменяется ведь всего лишь мнение компилятора об объекте но не сам объект. Но с другой стороны — перестраховаться захотелось — это мне казалось проще чем тратить время на изучение стандартов. Хотя если кто знает что по этому вопросу стоящее — буду благодарен за инфу.
Re[2]: Новый тип синглтона! Зацените!
От:
Аноним
Дата:
17.01.09 12:02
Оценка:
Здравствуйте, remark, Вы писали:
R>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.
Если честно, то я не догоняю. Подскажите пожалуйста, каким образом поток используя этот шаблон может получить указатель на недосозданный объект?
Ведь возврат из оператора new происходит только после того как объект полностью создан включая полное выполнение его конструктора. А до того как new отработает все потоки которые захотят указатель будут висеть на Sleep(0).
Re[3]: Новый тип синглтона! Зацените!
От:
Аноним
Дата:
17.01.09 12:26
Оценка:
Здравствуйте, Quasi, Вы писали:
Q>Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту.
Что вы имеете в виду под "барьерами памяти"? Чего-то не догнал, если честно.
Q>Для этого нужно использовать само значение указателя в качестве статуса, в этом случае барьер при доступе не нужен, т.к. будет иметь место "data dependency".
Тут вы пожалуй правы. Действительно, использование status — не так уж и необходимо.
Q>Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора.
Зачем?
Q>И еще вместо Sleep(0), лучше как в реализации boost::call_once под Win32 создавать уникальный именованный мьютекс и ждать на нем.
Не согласен. boost::call_once содержит глобальный объект который должен быть проинициализирован в момент первого вызова. Поэтому вместо boost::call_once лучше просто создать глобальный объект класса T — проблемы те же! Ведь собственно одно из главных преимуществ моего велика — пользователь не заморачивается с порядком инициализации глобальных объектов. По крайней мере в моём текущем проекте порядок инициализации огромной массы глобальных объектов — это одна большая язва которая время от времени даёт о себе знать багами которых приходится отлавливать очень долго.
Q>Естественно речь не идет о портируемости .
По поводу портируемости — чтобы мой велик портировать — нужно Interлокед функции реализовать для UNIX. Кому надо — могу выложить их реализацию под UNIX.
По поводу подходов альтернативных моему велику. Насколько я понял, практически везде используются критические секции для синхронизации доступа. А это тянет за собой кучу проблем. По сути проблемы с корректным созданием объекта мы заменяем проблемами с созданием критической секции, которые нисколько не лучше. Шило на мыло. Или я чего-то недогоняю?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, remark, Вы писали:
R>>Необходимо обеспечить корректное упорядочивание конструирования/считывания объекта и установки/считывания флага. В текущем варианте потоки могут работать с не полностью сконструированным объектом.
А>Если честно, то я не догоняю. Подскажите пожалуйста, каким образом поток используя этот шаблон может получить указатель на недосозданный объект? А>Ведь возврат из оператора new происходит только после того как объект полностью создан включая полное выполнение его конструктора. А до того как new отработает все потоки которые захотят указатель будут висеть на Sleep(0).
Либо компилятор может скомпилировать, либо аппаратное обеспечение может это выполнить допустим как (псевдо-код):
if (!InterlockedCompareExchange(&status, creating, sturtup))
{
ptr = (T*)malloc(sizeof(T));
T_constructor_part1(p);
status = created;
T_constructor_part2(p);
}
В текущем коде ничего не машает такому переупорядочиванию.
R>В текущем коде ничего не машает такому переупорядочиванию.
ООчень интересно! Спасибо! То есть по сути получается что компилятор может записать статус "created" ещё до окончания работы оператора new.
Я впервые об этих граблях слышу. Про переупорядочивание. А не дадите какуюнибудь ссылочку — когда и в каких случаях такой заподлянки можно ожидать? И какие основные способы есть с таким безобразием бороться?
R>
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Quasi, Вы писали:
Q>>Задумался тут, достаточно легко преобразовать данный велосипед в работающий без барьеров памяти при каждом доступе к сконструированному объекту.
А>Что вы имеете в виду под "барьерами памяти"? Чего-то не догнал, если честно. Q>>Только вот я думаю, перед разыменовыванием указателя нужен барьер для компилятора. А>Зачем?
приведены хорошие ссылки
Неплохая статья на эту тему
А>Не согласен. boost::call_once содержит глобальный объект который должен быть проинициализирован в момент первого вызова. Поэтому вместо boost::call_once лучше просто создать глобальный объект класса T — проблемы те же! Ведь собственно одно из главных преимуществ моего велика — пользователь не заморачивается с порядком инициализации глобальных объектов. По крайней мере в моём текущем проекте порядок инициализации огромной массы глобальных объектов — это одна большая язва которая время от времени даёт о себе знать багами которых приходится отлавливать очень долго.
BOOST_ONCE_INIT — The call_once function and once_flag type (statically initialized to BOOST_ONCE_INIT) can be used to run a routine exactly once
Поэтому никаких проблем и нет, тип once_flag — POD тип (в реализации интегральный тип, статически инициализируемый 0)
Единственное, что можно сделать лучше, так это избавиться от непроизводительного барьера памяти при каждом доступе к объекту
Здравствуйте, Аноним, Вы писали:
А>По поводу портируемости — чтобы мой велик портировать — нужно Interлокед функции реализовать для UNIX. Кому надо — могу выложить их реализацию под UNIX.
Интересно посмотреть , например, для всех платформ, на которых реализован Posix threads?