Легенда:
новое сообщение
закрытая нитка
новое сообщение
в закрытой нитке
старое сообщение
|
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
- Новичкам также крайне полезно ознакомиться с данным документом.
[C++] вот так было бы правильней, используя наследственность, ну... 21.12.07 04:40 Число просмотров: 2753
Автор: void <Grebnev Valery> Статус: Elderman Отредактировано 21.12.07 04:42 Количество правок: 1
|
> вот так было бы правильней, используя наследственность, ну > естественно надо сделать private copy constructor и > assignment operator. > Вообче то ето школьная задачка, не так ли?
Так :(... Я бы даже сказал детсадовская... Но тем неменее могут быть и шероховатости!
> class ITopic > { > public: > virtual ~ITopic(){}; > virtual _bstr_t GetValue() = 0; > };
Я уберу virtual в определении деструктора ~ITopic. Эти указатели указывают на структуры данных, которые должны постоянно находиться в памяти и обновляться, возможно, из других потоков по наступлении некоторых событий.
> class CTopicVector > { > private: > //I would use a smart pointer
Написал выше, что это нецелесообразно.
> //example: > //map<int , smart_ptr<ITopic> > m_TopicData > map<int , ITopic*> m_TopicData;
Возможно и не обойтись без map<int , ITopic*> vs vectior<Topic>. Но ... при использовании std::map мы можем сильно проиграть в производительности при большом числе элементов(~100 000) CTopicVector.
Ещё раз скажу о постановке задачи:
1)В начале цикла обработке CTopicVector::Size() == 0.
2) При поступлении обновлений данных, клиенты будут добавлять элементы при помощи AddTopic(ITopic* pTopic) в CTopicVector с указателями на свои структуры данных. Таким образом размер CTopicVector будет расти от нуля до некоторого значения N.
3)В некоторый момент (по таймеру, или условию N == MAX(N)) CTopicVector должен быть “flush” данные в некий процесс, и затем очищен. Функция, что ниже должна работать максимально быстро, чтобы не держать клиентов, обновляющих данные(ITopic, на некотором объекте синхронизации Lock()/Unlock():
STDMETHODIMP SomeObject::RefreshData( long *TopicCount,
SAFEARRAY **parrayOut)
{
...
// нечто вроде
Lock();
> int size = TopicVector.GetSize(); > for(int i = 0; i < size; ++i) > { > ITopic* pTopic = TopicVector.GetUnsafeTopic(i); > if(pTopic) > { > OutputDebugString(pTopic->GetValue()); > } > } ...
TopicVector.ClearVector()
Unlock();
}
Класс map куда более тяжелее в сравнении с vector при большом числе элементов (~100000) и при частом добавлении/удалении узлов. Напротив, для вектора можно “преаллокировать” сapacity(), так что освобождение вектора не будет приводить к фактической переаллокации памяти на глобальном хипе. В принципе, можно пойти дальше – создать массив вместо вектора в отдельном хипе. Пока не уверен, что это даст прирост производительности. Можно будет протестировать. И последнее, при интенсивных циклах создания/освобождения элементов TopicVector, при использовании vector или array не будет фрагментации памяти.
> CRITICAL_SECTION m_cs; > > public: > CTopicVector() > { > InitializeCriticalSection(&m_cs); > }; > virtual ~CTopicVector() > { > ClearVector(); > DeleteCriticalSection(&m_cs); > }; > > void ClearVector() > { > map<int , ITopic*>::iterator it; > EnterCriticalSection(&m_cs);
Не пойдёт, см. ниже.
> try > { > for(it = m_TopicData.begin();it != > m_TopicData.end();++it) > { > delete (*it).second; > } > m_TopicData.clear(); > }catch(...){}; > LeaveCriticalSection(&m_cs); > } > > int GetSize() > { > return (int)m_TopicData.size(); > } > int AddTopic(ITopic* pTopic) > { > int iId = (int)m_TopicData.size(); > EnterCriticalSection(&m_cs);
Не пойдёт, см. ниже.
> try > { > m_TopicData[iId] = pTopic; > }catch(...){} > LeaveCriticalSection(&m_cs); > return iId; > }; > //unsafe reference to an object > ITopic* GetUnsafeTopic(int iId) > { > ITopic *pTopic = NULL; > EnterCriticalSection(&m_cs); > try > { > pTopic = m_TopicData[iId]; > }catch(...){} > LeaveCriticalSection(&m_cs); > return pTopic; > } > > };
Указанное выше “ниже” - это то, что блокировка должна охватывать весь процесс SomeObject::RefreshData. Иначе, данные CTopicVector будут неактуальными или испорченными другими клиентскими потоками, которые ” в этот момент” пытаются обновить свои структуры(ITopic. Даже если и не придавать значения такой “рассинхронизации” (не гут), то некоторые обновления и вовсе могут быть потеряны, т.к. по выходу из SomeObject::RefreshData вектор CtopicVector очищается. Поэтому необходимы отдельные методы Lock()/Unlock(). Другая причина в том, что вызов EnterCriticalSection – это не бесплатный вызов, даже если объект не блокируется (если критическая секция не занята, то на EnterCriticalSection может приходится ~ 100 тактов ЦПУ). Ну а если, уж объект синхнонизации блокирован, то дело совсем плохо. Поэтому нельзя, на мой взгляд, использовать такие Lock-и внутри циклов. Надо делать это снаружи циклов.
И последняя шероховатость, которая не относится к эффективности, но к элегантности:
и у Вас и у Нас есть по-существу GetUnsafeTopic, как Вы справедливо заметили . Ключевое слово здесь Unsafe, и я не знаю что с этим делать. Похоже этой неэлегантной шероховатости не избежать, так чтоб не родить другую неэлегантность.
В целом я очень признателен Вам, даже несмотря на шероховатости обсуждаемых решений. Это придало мне уверенности, что тот код (не мой), который мне надо адаптировать – в топку и целиком.
|
|
|