Здравствуйте, Ryadovoy, Вы писали:
R>Идея была сделать устойчивую финальную версию, R>промежуточные версии нужны лишь для объяснения проблеммы, и они не тестировались в различных ситуациях. R>Или вы имели в виду финальную версию?
Я имею в виду первую версию. Я хотел сказать, что проблема не столько связана с многопоточностью. Копирование или какой-то другой механизм появился бы у нас ещё в однопоточной версии.
Здравствуйте, Ryadovoy, Вы писали:
R>Мне попадался проект в котором сплошь и рядом встречались конструкции try{}catch(...){} которые прятали реальные баги. R>программа не падала, но глючила страшно. R>Удалось ее нам привести к более менее стабильному состоянию лишь убрав все эти перехватчики.
Бесспорно бывают решения ещё хуже.
Вопрос только, сравнить свою программу с гораздо более плохой и сказать "Ну ничего у меня ещё не худший вариант". Или сравнить свою программу с более хорошей и сказать "А ведь можно сделать лучше".
Здравствуйте, Ryadovoy, Вы писали:
R>Правильно понял, что signals2 впервые появились в мае 2009-го года, R>и до этого были лишь signals, которые были не устойчивы к многопоточности?
Да.
R>Есть ли еще какие-нибудь альтернативы?
Думаю, есть.
Я не интересовался, мне не был нужен многопоточный обсервер. (Вообще, мне кажется, трудно быть уверенным в потокобезопасности, когда не уверен, сколько и каких коллбэков вызываются, даже если сама реализация обсервера безопасна.)
Здравствуйте, remark, Вы писали:
R>Это, конечно, достаточно печально. Выделять память, копировать N памяти, атомарно инкрементировать N счётчиков под мьютексом не айс.
В драйверах, где мне необходима была оптимизация, я избавился от копирования вектора, но та схема была на порядок сложнее.
Там было что-то вроде связного списка, который перебирается под локом (SpeenLock) и инкрементировался счетчик ссылок, а сам вызов происходил уже без лока.
И удаление ячейки из этого списка происходило тоже под этим локом, еще для чего-то была нужна переменная bRemove, уже не припомню.
Но там еще все это усугублялось необходимостью работать на разных уровнях IRQL (PASSIVE_LEVEL, DISPATCH_LEVEL).
Я хотел предоставить схему, которая будет предельно понятна, т.к. тогда при реализации такой схемы меньше шансов допустить ошибку.
Здравствуйте, Ryadovoy, Вы писали:
R>Если необходима обработка нехватки памяти, это делается, но не для примера в статье, вы со мной не согласны?
мне кажется, что зря вы так относитесь к своим "критикам", вам нужно быть сдержанным и мотать на ус ;=)
некоторые вещи режут глаза опытным разработчикам, потому что у них есть хороший багаж опыта, много книжек всяких прочитали от других профи
даже если вы не переносите исключения, то вы все равно могли бы сделать код более качественным с точки зрения поддержки
в этом вам помог бы механизм RAII
он удобен как при исключениях, так и исключение копи-паста, уменьшение _незначащего_ кода
язык С++ достаточно низкоуровневый и очень часто сложно за деревьями увидеть лес, то есть мелочи отвлекают и мешают понять суть
выработаны практики, который частично решают эти проблемы языка и вам их советуют
даже если вы лично не работаете с исключениями. вы могли бы написать код для клиентов, которые работают с ними
и ваш код не пострадал бы
вы могли заметить, что в некоторых функциях перед return у вас стоит Unlock
это и есть проблема в развивающемся коде (то есть не одноразовом коде студента, а коде, который используется\развивается в компании\команде из большого кол-ва разработчиков)
код в большой компании читают очень много, и он должен быть понятен
можно забыть сделать Unlock и это усложняет поддержку кода
именно поэтому в C использовали идиому single function exit point : http://c2.com/cgi/wiki?SingleFunctionExitPoint (чтобы в самом конце освобождать ресурсы)
в C++ это решили через RAII (инструмент не идеальный, ибо плохо дружит с исключениями, но это отдельная холиворная тема, которые тут уже не раз перемалывали)
попробуйте хотя бы частично всосать те советы, которые вам дают
успехов
Здравствуйте, Ryadovoy, Вы писали:
ЮЖ>>Теоритически эти проблемы могут возникнуть при возбуждении std::bad_alloc при вставке в вектор (CNotificationDispatcher::Subscribe) :
R>Если необходима обработка нехватки памяти, это делается, но не для примера в статье, вы со мной не согласны?
Здесь не нужна обработка нехватки памяти. Только RAII для lock/unlock. Как миниму в силу того, что на момент написания неизвестны сценарии использования.
R>Наши user-level приложения просто падают когда заканчивается память, и создается креш дамп, ибо странно что закончилась память, неправда-ли?
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здесь не нужна обработка нехватки памяти. Только RAII для lock/unlock. Как миниму в силу того, что на момент написания неизвестны сценарии использования.
Рои не панацея, и иногда уменьшают читабельность и понимание происходящего.
Здравствуйте, uzhas, Вы писали:
U>мне кажется, что зря вы так относитесь к своим "критикам", вам нужно быть сдержанным и мотать на ус ;=)
Если я был с кем-то резок, прошу извинения, я просто пытаюсь поддерживать беседу.
U>некоторые вещи режут глаза опытным разработчикам, потому что у них есть хороший багаж опыта, много книжек всяких прочитали от других профи
Опыт может быть в разных областях программирования, использования разных библиотек и т.п.
и соответственно глаза резать могут разные вещи в зависимости от того, в какой области программирования кто работает,
и с какими библиотеками кто привык работать, даже от соглашения оформления кода может много чего зависить.
Давайте в конце концов не будем меряться опытом?
U>даже если вы не переносите исключения, то вы все равно могли бы сделать код более качественным с точки зрения поддержки U>в этом вам помог бы механизм RAII
Этот механизм все же "на любителя"
U>код в большой компании читают очень много, и он должен быть понятен
Механизм RAII не улучшает читабельность кода, и понимание его, что более важно для статьи.
Здравствуйте, Ryadovoy, Вы писали:
ЮЖ>>Здесь не нужна обработка нехватки памяти. Только RAII для lock/unlock. Как миниму в силу того, что на момент написания неизвестны сценарии использования.
R>Рои не панацея, и иногда уменьшают читабельность и понимание происходящего.
Каким образом переписывание такого кода на вариант с использованием RAII уменьшит читабельность?
+ потенциальных мест для ошибок в этом коде больше чем в аналоге с RAII.
+
Если тебе мешает дополнительный scope, то можно переписать код так:
R>void SendNotification(void* pContext)
{
ScopeLocker scopeLocker(&m_cs);
CNotificationListenerPtrList vListeners = m_vListeners;
scopeLocker.release(); // or you may call it "unlock"for(size_t i = 0; i < vListeners.size(); ++i)
{
vListeners[i].OnNotification(pContext);
}
}
Здравствуйте, Ryadovoy, Вы писали:
U>>код в большой компании читают очень много, и он должен быть понятен R>Механизм RAII не улучшает читабельность кода, и понимание его, что более важно для статьи.
Однажды мне попался код из проекта одного уважаемого разработчика:
Само собой, это упрощенная схема — реальный код был более развесистый. Память не текла лишь при удачном стечении обстоятельств. После замены на RAII оно стало выглядеть вот так:
GuardResource1();
if (!CheckResource1())
return false;
GuardResource2();
if (!CheckResource2())
return false;
GuardResource3();
if (!CheckResource3())
return false;
...
return true;
Конечно, всех проблем это не решило, но темпы утечек серьезно уменьшились. Пострадала ли читабельность?
Здравствуйте, Ryadovoy, Вы писали:
R>Здравствуйте, rg45, Вы писали:
R>>Общая идея понятна. Детально код не изучал, но что сразу резануло по глазам — небезопасность кода с точки зрения исключений — повсеместно в коде встречаются фрагменты: R>>
R>>EnterCriticalSection();
R>>//Какие-то операции
R>>LeaveCriticalSection();
R>>
R>>Что если во время выполнения "каких-то операций" возникнет исключение? Почему бы в данном случае не использовать RAII — например, мьютексы и локи из boost::thread?
R>Вы конечно-же со мной не согласитесь, но я считаю что c++ исключения это зло.
а можно узнать почему??? какие ваши аргументы?
R>Я работаю в команде, где не особо ими пользуются (только при необходимости),
мда бывает неприятность... ;( мои соболезнования
R>а необходимости из под колбека выбрасывать исключение нет никакой. R>bools и stl может выбросить исключение, но оно возможно лишь когда имеет место ошибка программиста R>(ну или закончилась память в системе, что тоже критично), тогда нужно падать с креш репортом, дабы не скрывать ошибку.
для 90х это было бы ОК, но на дворе-то 2010й
зачем было разводить столько графоманства, когда современными средствами задача решается тривиально?
пруф-код, слепленый за 5 ммин
struct Subscriber {
void OnSomething(int asContext) { printf("%i\n", asContext); }
};
int _tmain(int argc, _TCHAR* argv[])
{
std::set<Subscriber*> subscribers;
subscribers.insert(new Subscriber());
int context = 7;
task_group g;
g.run( [subscribers,context] { for each(auto s in subscribers) { s->OnSomething(context); }} );
g.wait();
return 0;
}
Здравствуйте, Ryadovoy, Вы писали:
U>>даже если вы не переносите исключения, то вы все равно могли бы сделать код более качественным с точки зрения поддержки U>>в этом вам помог бы механизм RAII R>Этот механизм все же "на любителя"
Это общепринятый механизм.
Ручные EnterCriticaLSection, LeaveCriticalSection и иже с ними у нас являются поводом к прекращени code review и отправкой его на доработку.
Здравствуйте, Ryadovoy, Вы писали:
R>Здравствуйте, Alexander G, Вы писали: AG>>ну, когда мне такое понадобится, я скорее посмотрю на boost::signals2 R>Спасибо за наводку, я обязательно обращу внимание на signals2.
Имхо, надо было с этого начать. В диссертациях не зря первая глава всегда — это обзор существующих наработок по теме.
R>Моя статья направлена не на то, чтобы дать готовое решение, R>она объясняет основные принципы, которые применимы там, где boost бывает недоступен (драйвера, нейтивные приложения и т.п.)
с чего бы это буст был там недоступен? и что такое "нейтивные приложения" в контексте С++?
Здравствуйте, jazzer, Вы писали:
J>Имхо, надо было с этого начать. В диссертациях не зря первая глава всегда — это обзор существующих наработок по теме.
Спасибо за замечание, я думаю добавить в статью что-то вроде этого:
По сути я не выдумывал новое решение, я использовал готовый шаблон проектирования,
этот же шаблон использовали и создатели boost, но они разрабатывают именно библиотеку, а не решение для частной ситуации.
boost это огромное количество кода, такое количество кода необходимо, если писать универсальную библиотеку, для частного решения может подойти более простой вариант,
и из-за этого он будет более понятным и легко реализуемым.
Вы досконально понимаете как устроена библиотека signal2 в boost?
лично меня пугает количество кода при вызове колбека, вот стек вызовов.
J>с чего бы это буст был там недоступен? и что такое "нейтивные приложения" в контексте С++?
Ну я считаю что многопоточность это уже не совсем контекст c++, возможно я выбрал не совсем ту ветку форума, но более подходящей не нашел.
Native приложение это приложение, которое может запускатья на ранней стадии загрузки виндовс (например консоль восстановления)
виндовый checkdisk так работает, он запускает c:\WINDOWS\system32\autochk.exe на ранней стадии загрузки. autochk.exe это и есть Native приложение.
Native приложение в основном пользуется лишь экспортом из ntdll.dll (недокументированными функциями), и ничего другого ему не доступно.
У нас по началу были большие трудности с CRT и STL, а вы про boost говорите...
Я Windows программист, и никогда не занимался вопросами переносимости кода на другие платформы,
но думаю что и на других платформах существуют схожие проблемы.
Интересно можно ли при написании модулей ядра в Linux пользоваться библиотеками STL и boost?
Я думаю что все зависит от кривизны рук, а не от применяемого инструмента или от технологии.
Если разработчик с кривыми руками нужно его гнать в шею и нанять толкового программиста.
Не думайте что я не буду использовать RAII если они будут нужны,
как доказательство могу привести пример моего личного их использования
for(;;)
{
{ //Enter to notify thread critical section
CGuard aGuard(m_csNotifyThread);
if(m_vFilesNotifyQueue.empty())
{
if(!str)
{
//Scan was completed
CGuard aGuard(m_csScanState);
m_nScanState = IFRClient::NotStarted;
}
break;
}
}
if(WaitForSingleObject(m_hNotifyThread, 100) != WAIT_TIMEOUT)
{
//Something happends with notitification thread, that is error case
_ASSERT(FALSE);
CGuard aGuard(m_csScanState);
m_nScanState = IFRClient::NotStarted;
break;
}
}
Здравствуйте, landerhigh, Вы писали:
L>Здравствуйте, Ryadovoy, Вы писали:
U>>>даже если вы не переносите исключения, то вы все равно могли бы сделать код более качественным с точки зрения поддержки U>>>в этом вам помог бы механизм RAII R>>Этот механизм все же "на любителя"
L>Это общепринятый механизм.
L>Ручные EnterCriticaLSection, LeaveCriticalSection и иже с ними у нас являются поводом к прекращени code review и отправкой его на доработку.
Я думаю что тематика RAII к данной статье притянута за уши, давайте создадим тему RAII и будем это там это обсуждать?
я так понимаю это вопрос веры для многих поклонников RAII,
по этому я подумаю над тем, чтобы добавить RAII в примеры,
дабы избежать в дальнейшем коментариев насчет RAII.
Здравствуйте, Ryadovoy, Вы писали:
R>Вы конечно-же со мной не согласитесь, но я считаю что c++ исключения это зло. R>Я работаю в команде, где не особо ими пользуются (только при необходимости), а необходимости из под колбека выбрасывать исключение нет никакой. R>bools и stl может выбросить исключение, но оно возможно лишь когда имеет место ошибка программиста R>(ну или закончилась память в системе, что тоже критично), тогда нужно падать с креш репортом, дабы не скрывать ошибку.
Если класс представлен здесь, значит его предлагается использовать, что в свою очередь влечёт некорректность навязывания своего стиля пользователям.
По-моему, если
1) корректно разрешите проблему безопасности исключений
2) избавитесь от связи (время жизни диспетчера) == (время, проведенное в критической секции)
3) гарантируете отсутствие утечки ресурса "объект-синхронизации" (сейчас это критическая секция)
за приведенный код Вам можно будет сказать спасибо.