Легенда:
новое сообщение
закрытая нитка
новое сообщение
в закрытой нитке
старое сообщение
|
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
- Новичкам также крайне полезно ознакомиться с данным документом.
|
Это точно... Вопрос старый и весьма избитый. Наиболее... 17.09.08 07:35 Число просмотров: 2497
Автор: void <Grebnev Valery> Статус: Elderman
|
> Даже не вопрос... Это точно... Вопрос старый и весьма избитый. Наиболее содержательный ответ можешь най ти в очётах Мейра и Александреску - "Double-Checked Locking, Threads,Compiler Optimizations,and More Scott Meyers, Ph.D. Software Development Consultant (Based on Work with Andrei Alexandrescu.)"
Неплохое ридинг такогоже уровня - "Double-Checked Locking An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects Douglas C. Schmidt, Tim Harrison".
Всё остальное (статьи)- глупости и весьма пустые вариации на эту тему, ИМХО. В статьях, что выше ты найдёшь такой вывод - в VS 2005 - легко сделать (см. Мейра), поскольку volatile реализовано на memory barrier (в отличие от предыдущих версий копиллеров).
|
<programming>
|
[C++] По реализации синглетона вопрос 17.09.08 00:37
Автор: PS <PS> Статус: Elderman Отредактировано 17.09.08 01:24 Количество правок: 1
|
Даже не вопрос...
Есть такая реализация:
class S
{
public:
static S& Instance()
{
static S inst;
return inst;
}
private:
....
};
---
Так вот, реализация вполне валидная, красивая, короткая - всем хороша.
Только вот иногда не работает.
Сегодня второй раз за три месяца заметил, что в объекте пропадают мемберы - был map заполнен, а на следующей строке - он уже пуст. Был мембер-указатель валидным, не трогали его, а он уже непонятно куда указывает.
Симптомы проявлялись когда:
1.Синглетон объявлен и реализован в exe. А тип какого нибудь мембера (он даже может быть указателем) объявлен и реализован в dll.
2. Оба exe (оба раза) это были сервисы.
3. Оба синглетона были еще и потоками: void Start() { new boost::thread( ref(*this)); } Кстати, черти проявляются именно после вызова Start(). Но, не факт что это причина.
На настройки проекта не грешить - выравнивания, (/MD) и пр. - все корректно и совпадает.
И это не проблема "двойного синглетона" - объект действительно один и тот же.
Порушенный стек, куча - отпадают. Код полностью safe (если, конечно, stl и boost можно считать safe). к тому же при поиске причины - код был сведен к минимуму: осталась только иницализация сервиса, инициализация синглетона, и Start.
Лечится просто: либо переносишь этот класс в dll, либо отказываешся от его синлетонности и делаешь объект, скажем, мембером основного класса приложения.
Чешу репу.
Кто нибудь с таким сталкивался?
P.S.
Чую, что дело бесовское, а обосновать не могу.
|
|
ПИП! ПИП! ПИП! ПИП! 17.09.08 18:53
Автор: PS <PS> Статус: Elderman
|
тут много много мата...
тут очень много мата...
тут столько мата, что форумная чекилка сломалась, у боцманов завяли уши, а воробьи упали мертвыми на землю.
ПИП! ПИП! ПИП! ПИП!
Весь день гонялся за багой: сначала избавился от потоков. Стабильно не работает.
Потом избавился от остальных объектов. Стабильно не работает.
Избавился от сервиса, и сделал консоль. Стабильно не работает.
В синглетоне оставил только один метод. Стабильно не работает.
Код девственно чист, есть только синглетон который выводит строчку в файл. Имя файла задается ф-ией Init(). Потоков нет, буста нет, консольное приложение - стабильно не работает. Имя файла между Init() и Write() исчезает, видимо в параллельное пространство.
ПИП!ПИП!ПИП!ПИП!
Заменил /O2 на /Od
Заработало!
ПИП!ПИП!ПИП!ПИП!
Сцука оптимизатор.
|
| |
А если локальную статическую переменную объявить еще и... 18.09.08 03:42
Автор: amirul <Serge> Статус: The Elderman
|
А если локальную статическую переменную объявить еще и волатильной?
|
|
Это точно... Вопрос старый и весьма избитый. Наиболее... 17.09.08 07:35
Автор: void <Grebnev Valery> Статус: Elderman
|
> Даже не вопрос... Это точно... Вопрос старый и весьма избитый. Наиболее содержательный ответ можешь най ти в очётах Мейра и Александреску - "Double-Checked Locking, Threads,Compiler Optimizations,and More Scott Meyers, Ph.D. Software Development Consultant (Based on Work with Andrei Alexandrescu.)"
Неплохое ридинг такогоже уровня - "Double-Checked Locking An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects Douglas C. Schmidt, Tim Harrison".
Всё остальное (статьи)- глупости и весьма пустые вариации на эту тему, ИМХО. В статьях, что выше ты найдёшь такой вывод - в VS 2005 - легко сделать (см. Мейра), поскольку volatile реализовано на memory barrier (в отличие от предыдущих версий копиллеров).
|
|
Ты гарантируешь первый вызов Instance из однопоточного [upd] 17.09.08 04:16
Автор: amirul <Serge> Статус: The Elderman Отредактировано 17.09.08 04:19 Количество правок: 1
|
> Чешу репу. > Кто нибудь с таким сталкивался?
Ты гарантируешь первый вызов Instance из однопоточного окружения?
> P.S. > Чую, что дело бесовское, а обосновать не могу.
-------------
Кроме того, по хорошему стоило бы захватывать какую нибудь критическую секцию в самом начале S::Instance
|
| |
Да, гаранитую 17.09.08 12:15
Автор: PS <PS> Статус: Elderman
|
Да, первый вызов Instance() идет только из одного потока.
Код примерно следующий.
S1::Instance().Init()
S2::Instance().Init()
S3::Instance().Init()
...
S1::Instance().Start()
Первые вызовы идут в одном потоке.
Остальные потоки появляются только после инициализации всех синглетонов.
void тут высказывается про ровные места. Если бы они были бы ровными - поста бы не появилось.
У меня появилось другое предположение. Давно заметил, что boost::thread ведет себя не корректно в "кислотной среде".
Есть у меня код, доставшийся по наследству: он полностью unsafe, буфера, которые могут переполнится, указатели пущенные в свободное плавание, доступ к переменным из потоков не залоченые, и все остальные прелести.
Так вот в этом коде new boost::thread вываливается с эксепшеном.
Так что его можно использовать как детектор "что-то где-то плохо".
Так вот, не создал ли я где то в своем safe коде, какой нить косяк... буду разбираться. Если косяк действительно есть, то удалю этот пост. Если нарвусь на что-то более интересное - напишу.
|
| | |
Да, гаранитую ? 17.09.08 18:09
Автор: void <Grebnev Valery> Статус: Elderman
|
> void тут высказывается про ровные места. Если бы они были > бы ровными - поста бы не появилось.
In the simplest case you might try this. No?
////////////////////////////////////////////////////////////////////
// My_class.cpp
class My_class
{
public:
static My_class& unsafe_instance()
{
static My_class inst;
return inst;
}
bool initialize(void)
{
bool rc = false;
// TO DO
return rc;
}
void deinitialize(void)
{
// TO DO before dtor()
}
private:
My_class()
{
}
~My_class()
{
}
My_class& operator =(const My_class&);
};
static struct init_my_class
{
init_my_class() : m_instance(My_class::unsafe_instance())
{
m_initialized = m_instance.initialize();
}
~init_my_class()
{
m_instance.deinitialize();
}
bool m_initialized;
My_class& m_instance;
init_my_class& operator=(const init_my_class&);
} _init_my_class_;
void initialize_my_classes(void)
{
if (false ==init_my_classm_initialized)
{
throw std::exception("an error at init_my_class startup");
}
/* TO DO
else if ()
{
throw std::exception("an error at ....");
}
*/
else
{
}
}
////////////////////////////////////////////////////////////////////
// main.cpp
int _tmain(int argc, _TCHAR* argv[])
{
try
{
// there is only a place where you initialize/check your statics
initialize_my_classes();
}
catch(std::exception& e)
{
printf("Failed to initialize my staff (%s)", e.what());
return 1;
}
catch(...)
{
printf("Failed to initialize my staff (unhandled exception at startup)");
return 1;
}
return 0;
}
---
|
| |
Если бы он мог гарантировать - то он не писал бы про... 17.09.08 07:45
Автор: void <Grebnev Valery> Статус: Elderman
|
> > Чешу репу. > > Кто нибудь с таким сталкивался? > > Ты гарантируешь первый вызов Instance из однопоточного > окружения? Если бы он мог гарантировать - то он не писал бы про "чудеса" на ровном месте.
|
| | |
Я наверное чего то недопонял 17.09.08 11:17
Автор: amirul <Serge> Статус: The Elderman
|
> > Ты гарантируешь первый вызов Instance из однопоточного > > окружения? > Если бы он мог гарантировать - то он не писал бы про > "чудеса" на ровном месте.
А что мешает вызвать S::Instance из первого потока, чтобы проинициализировать статическую переменную и уж потом создавать новые? Кроме того, однопоточность функции можно гарантировать простенькой критической секцией (лучше завернутой в объект - чтоб не следить за освобождением)
Или проблема в чем то еще?
|
| | | |
If he does exactly this way, there should be no problem. 17.09.08 17:36
Автор: void <Grebnev Valery> Статус: Elderman
|
|
|
[C++] а точно один? 17.09.08 01:50
Автор: dl <Dmitry Leonov>
|
> Сегодня второй раз за три месяца заметил, что в объекте > пропадают мемберы - был map заполнен, а на следующей строке > - он уже пуст. Был мембер-указатель валидным, не трогали > его, а он уже непонятно куда указывает. > Симптомы проявлялись когда: > 3. Оба синглетона были еще и потоками: void Start() { new > boost::thread( ref(*this)); } Кстати, черти проявляются > именно после вызова Start(). Но, не факт что это причина. > И это не проблема "двойного синглетона" - объект > действительно один и тот же.
Если воткнуть отладочную печать/брекпойнт в конструктор S и конструктор класса мембера, лишние заходы туда не проявляются?
|
| |
Точно. Только узнавал подругому: сделал S* Instance(), 17.09.08 01:59
Автор: PS <PS> Статус: Elderman Отредактировано 17.09.08 02:16 Количество правок: 3
|
Точно. Только узнавал подругому: сделал S* Instance(),
а перд вызовами вывожу в лог указатель.
void S::operator()()
{
S* tmp = S::Instance();
Log::Instance()->Trace( (long)tmp );
vector<string> t1 = S::Instance()->t; // тут все ок.
tmp = S::Instance();
Log::Instance()->Trace( (long)tmp );
vector<string> t2 = S::Instance()->t; // тут все ок.
while( true )
{
tmp = S::Instance();
Log::Instance()->Trace( (long)tmp );
vector<string> t3 = S::Instance()->t; // а тут бред!
}
}
---
первый, второй и третий лог - выдает одно и то же.
Кстати, здесь видно что я из потока обращаюсь к мемберам на прямую. Это валидно, т.к. запись в них происходит только в Init(); которая вызывается до Start();
Больше никто не трогает эти мемберы (ни на запись, ни на чтение).
> Если воткнуть отладочную печать/брекпойнт в конструктор S и > конструктор класса мембера, лишние заходы туда не > проявляются?
|
|
|