Легенда:
   новое сообщение
    закрытая нитка
    новое сообщение
    в закрытой нитке
    старое сообщение
         
		 | 
- Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
 - Новичкам также крайне полезно ознакомиться с данным документом.
   
  |   |   | 
Нууу, брат ты погнал, "Я уберу virtual в определении...  21.12.07 21:05  Число просмотров: 2523
 Автор: + <Mikhail> Статус: Elderman
 | 
 
> > вот так было бы правильней, используя > наследственность, ну > > естественно надо сделать private copy constructor и > > assignment operator. > > Вообче то ето школьная задачка, не так ли?  >  > Так :(... Я бы даже сказал детсадовская... Но тем неменее > могут быть и  шероховатости! >  > > class ITopic > > { > > public: > >	 virtual ~ITopic(){}; > >	 virtual _bstr_t GetValue() = 0; > > }; >  > Я уберу  virtual в определении деструктора ~ITopic.  Нууу, брат ты погнал, "Я уберу 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 не будет фрагментации > памяти. 
 Фрагментация будет по любому, если ты конечно не преаллоцируешь память для твоих Topic objects
 
 >  >  > >	 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. Даже если и не 
 поетому smart poiter и нужен
 
 > придавать значения  такой “рассинхронизации” (не гут), то > некоторые обновления и вовсе могут быть потеряны, т.к. по > выходу из  SomeObject::RefreshData  вектор CtopicVector > очищается. Поэтому необходимы отдельные методы > Lock()/Unlock(). Другая причина в том, что вызов > EnterCriticalSection – это не бесплатный вызов, даже если > объект не блокируется (если критическая секция не занята, > то на EnterCriticalSection может приходится ~ 100 тактов > ЦПУ). Ну а если, уж объект синхнонизации блокирован, то > дело совсем плохо. Поэтому  нельзя, на мой взгляд, > использовать такие Lock-и внутри циклов. Надо делать это > снаружи циклов. > И последняя шероховатость, которая не относится к > эффективности, но к элегантности: > и у Вас и у Нас  есть по-существу GetUnsafeTopic, как Вы > справедливо заметили . Ключевое слово здесь Unsafe, и я не > знаю что с этим делать. Похоже этой неэлегантной > шероховатости не избежать, так чтоб не родить другую > неэлегантность. >  
 поетому smart poiter и нужен
 
 
 > В целом я очень признателен Вам, даже несмотря на > шероховатости обсуждаемых решений. Это придало мне > уверенности, что тот код (не мой), который мне надо > адаптировать  –  в топку и целиком. 
 показал бы ТОТ код , который "не нужен"
 | 
 
| 
<programming>
 |  
 
[C++] Получилось хорошее решение (но я испортил нитку в форуме).  19.12.07 07:50  
 Автор: void <Grebnev Valery> Статус: Elderman Отредактировано 28.12.07 08:17  Количество правок: 4
 | 
 
Прошу прощения, но я испортил нитку. Я хотел "ответить" на свой постинг  в корень но каким -то образом затёр своё первое сообщение. Администраторы/модераторы -  я не хотел..... ;( Если можно, то исправьте плз.
 -----
 
 В общем получилось весьма универсальное и производительное решение, см ниже.
 Не map, а всё ж вектор. Работает быстро. Решение годится (протестировано) для 2-х типов задач:
 
 - когда для обновлений добавляются относительно "статичные" топики, например, статусные переменные, результаты запросов к базам данных  и т.д. Такие топики могут быть добавлены в вектор при помощи TopicVector::AddTopic и обновлены Subscriber-ом по таймеру.
 
 - когда обновляются относительно "быстрые" топики 10000-15000 раз в секунду, например, данные для Microsoft RTD (real time data server). Не вдаваясь в обсуждение майкрософтовской трактовки "real time data", скажу, что данные накапливаются в TopicVector по приходу обновлений для топика при помощи TopicVector::UpdateTopic. Главный трюк - когда приходит обновление  устанавливаем topic->m_cRef ++ , а когда Subscriber получает обновления из TopicVector, то он сбрасывает эти значения  topic->m_cRef = 0.
 Таким образом, вектор содержит единственное "вхождение" топика (как map), а данные всегда актуальны. Это в разы быстрее, чем map.
 
 В принципе, по результатам тестов - решение весьма производительно. Если же надо будет быстрее, то, думаю, вектор можно будет заменить массивом (может и на отдельном хипе). Сделать это будет легко, поскольку для TopicVector не предусматривается операции удаления/вставки отдельного элемента, а только добаление в хвост и очистка всего вектора.
 
 
 #include <string>
 #include <vector>
 #include "comdef.h"
 
 const int INVALIDE_TOPIC_ID = -1;
 const int TOPICVECTOR_RESERVE_SIZE = 500000;
 
 typedef variant_t topic_t;
 
 class Topic {
 public:
 	virtual topic_t GetValue(void) const PURE;
 	Topic(long ID = INVALIDE_TOPIC_ID) : m_topicID(ID), m_cRef(0L) {};
 	virtual ~Topic(){};
 	long m_topicID;
 	long m_cRef;
 };
 
 class TopicVector
 {
 public:
 	void AddTopic(Topic* topic)
 	{
 		m_vector.push_back(topic);
 		topic->m_cRef ++;
 	}
 	void UpdateTopic(Topic* topic)
 	{
 		if ( 0 == topic->m_cRef)
 			m_vector.push_back(topic);
 		topic->m_cRef ++;
 	}
 	size_t Size(void) const
 	{
 		return m_vector.size();
 	}
 	bool IsValidTopic(const size_t idx) const
 	{
 		if (NULL != m_vector.at(idx) &&	INVALIDE_TOPIC_ID != m_vector.at(idx)->m_topicID )
 			return true;
 		else
 			return false;
 	}
 	Topic* operator[](const size_t idx) 
 	{
 		return m_vector.at(idx);
 	}
 	void Lock(void) 
 	{
 		::EnterCriticalSection(&m_cs);
 	}
 	void Unlock(void)
 	{
 		::LeaveCriticalSection(&m_cs);
 	}
 	void Clear(void)
 	{
 		m_vector.clear();
 	}
 	TopicVector()
 	{
 		::InitializeCriticalSection(&m_cs);
 		m_vector.reserve(TOPICVECTOR_RESERVE_SIZE);
 	}
 	~TopicVector()
 	{
 		::DeleteCriticalSection(&m_cs);
 	}
 
 private:
 	std::vector<Topic*>  m_vector;
 	CRITICAL_SECTION m_cs;
 };
 | 
 
 
  | 
вот так было бы правильней, используя наследственность, ну...  20.12.07 02:13  
 Автор: + <Mikhail> Статус: Elderman
 | 
 
вот так было бы правильней, используя наследственность, ну естественно надо сделать private copy constructor и assignment operator.
 Вообче то ето школьная задачка, не так ли? 
 
class ITopic
{
public:
    virtual ~ITopic(){};
    virtual _bstr_t GetValue() = 0;
};
class CTopicVector
{
private:
    //I would use a smart pointer 
    //example:
    //map<int , smart_ptr<ITopic> > m_TopicData
    map<int , ITopic*> m_TopicData;
    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;
    }
};
class CTopic1: public ITopic
{
private:
    _bstr_t m_bstrVal;
    int     m_iVal;
public:
    CTopic1(_bstr_t bstrVal, int iVal)
        :m_bstrVal(bstrVal),m_iVal(iVal){}
    _bstr_t GetValue()
    {
        return m_bstrVal + L":" + _bstr_t(_variant_t(m_iVal)) + L"\n";
    }
};
class CTopic2: public ITopic
{
private:
    double  m_dVal;
    bool    m_bVal;
public:
    CTopic2(double dVal, bool bVal)
        :m_dVal(dVal),m_bVal(bVal){}
    _bstr_t GetValue()
    {
        return _bstr_t(_variant_t(m_dVal)) + L":" + (m_bVal?L"true\n":L"false\n");
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    CTopicVector TopicVector;
    TopicVector.AddTopic(new CTopic1(L"Topic1",1));
    TopicVector.AddTopic(new CTopic1(L"Topic1",2));
    TopicVector.AddTopic(new CTopic1(L"Topic1",3));
    TopicVector.AddTopic(new CTopic2(3.14,true));
    int size = TopicVector.GetSize();
    for(int i = 0; i < size; ++i)
    {
        ITopic* pTopic = TopicVector.GetUnsafeTopic(i);
        if(pTopic)
        {
            OutputDebugString(pTopic->GetValue());
        }
    }
    return 0;
}
 ---
 > Буду рад и благодарен, если кто-нить подскажет элегантное > решение. Задача такова: > Есть произвольные структуры данных (векторы, массивы и т.д) > classA {...} > classBB {...} > .... >  > и есть векторы, массивы, мапы этих структур >  > vector<A> va; > vector<BB> vb; > ... >  > Каждый экземпляр таких структур/классов может > быть”отображён” на более простой тип, например, строку, > целое, булево и т.д. Т.е. Для каждого класса может быть > определена некоторая функция, типа virtual variant_t > GetTopicValue(const LPVOID pdata) const. >  >  > Существует поток, в котором надо единообразно периодически > “пробегать” по списку (контейнеру ссылок) всех этих > элементов, с тем чтобы в цикле получать разультат > “отображения” каждого экемпляра списка, например, в > псевдокоде надо выполнить: >  > loop for each in (va,vb, ...) > v = each.GetTopicValue(NULL); >  > Другие потоки добавляют в данный список ссылки на > существующие структуры, например, когда данные обновлены. >  > Я смотрел аналогичный код некоторых гурей, у которых я > учусь– мне плохо стало от крутизны, непонятности, > темплейтности, объектно ориентированности и ненужности. > Попробовал сделать сам – получается просто, но хочется ещё > более просто, изящно и элегантно. Попробовал темлейты > прикрутить (как у гурей) – не получается чтоб это нужно > было (от недостатка моих знаний, видимо). Наколеночный код > у меня таков: >  > #include "stdafx.h" > #include <string> > #include <vector> > #include <sstream> > #include "comdef.h" >  >  > /////////////////////////////////////////////////////////// > ////// > // > // Generic topic containers > // > /////////////////////////////////////////////////////////// > ////// >  > interface ITopic { > 	virtual variant_t GetTopicValue(const LPVOID pdata) > const PURE; > 	virtual ~ITopic(){}; > }; >  > class Topic { > public: > 	virtual variant_t GetTopicValue(const LPVOID pdata) > const > 	{ > 		return (m_topicInterface ? > m_topicInterface->GetTopicValue(pdata) : variant_t()); > 	} > 	Topic() : m_topicID(0L), m_topicInterface(NULL) {} > 	long	m_topicID; > 	ITopic* m_topicInterface; > }; >  > class TopicVector > { > public: > 	void AddTopic(const long ID, ITopic* item) > 	{ > 		Topic topic; > 		topic.m_topicID = ID; > 		topic.m_topicInterface = item; > 		m_vector.push_back(topic); > 	} > 	size_t Size(void) const > 	{ > 		return m_vector.size(); > 	} > 	Topic& operator[](const size_t idx)  > 	{ > 		return m_vector.at(idx); > 	} > 	void Lock(void)  > 	{ > 		::EnterCriticalSection(&m_cs); > 	} > 	void Unlock(void) > 	{ > 		::LeaveCriticalSection(&m_cs); > 	} > 	TopicVector() > 	{ > 		::InitializeCriticalSection(&m_cs); > 	} > 	~TopicVector() > 	{ > 		Lock(); > 		for (size_t i = 0; i < m_vector.size(); > i ++) > 			delete > m_vector.at(i).m_topicInterface; > 		Unlock(); > 		::DeleteCriticalSection(&m_cs); > 	} > private: > 	std::vector<Topic> m_vector; > 	CRITICAL_SECTION m_cs; > }; >  > /////////////////////////////////////////////////////////// > ////// > // > // Example: concrete topics > // > /////////////////////////////////////////////////////////// > ////// >  > class ConcreteTopicA: public ITopic { > public: > 	virtual variant_t GetTopicValue(const LPVOID data) > const > 	{ > 		std::ostringstream s; > 		s << m_sval << ":"	<< > m_ival; > 		return _bstr_t(s.str().c_str()); > 	} > 	ConcreteTopicA(const std::string& sval, const > int ival): m_sval(sval), m_ival(ival) {}; > 	virtual ~ConcreteTopicA() {}; > private: > 	std::string m_sval; > 	int			m_ival; > }; >  > class ConcreteTopicB: public ITopic { > public: > 	virtual variant_t GetTopicValue(const LPVOID data) > const > 	{ > 		std::ostringstream s; > 		s << m_bval << ":"	<< > m_fval; > 		return _bstr_t(s.str().c_str()); > 	} > 	ConcreteTopicB(const bool bval, const double& > fval): m_bval(bval), m_fval(fval) {}; > 	virtual ~ConcreteTopicB() {}; > private: > 	bool	m_bval; > 	double	m_fval; > }; >  >  > int _tmain(int argc, _TCHAR* argv[]) > { > 	// create concrete topics > 	ConcreteTopicA* topicA = new ConcreteTopicA("some > string value", 100); > 	ConcreteTopicB* topicB = new ConcreteTopicB(true, > 1000.0); >  > 	// add topics > 	TopicVector topics; > 	topics.Lock(); >  > 	topics.AddTopic(1, topicA); > 	topics.AddTopic(2, topicB); >  > 	//print it > 	variant_t v; > 	for (size_t i = 0; i < topics.Size(); i ++) { > 		v = topics[i].GetTopicValue(NULL); > 		// TO DO > 	} >  > 	topics.Unlock(); >  > 	return 0; > } >  > Спасибо, если кто предложит красивое, простое решение.
 | 
 
 
  |   | 
[C++] вот так было бы правильней, используя наследственность, ну...  21.12.07 04:40  
 Автор: 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, и я не знаю что с этим делать. Похоже этой неэлегантной шероховатости не избежать, так чтоб не родить другую неэлегантность.
 
 В целом я очень признателен Вам, даже несмотря на шероховатости обсуждаемых решений. Это придало мне уверенности, что тот код (не мой), который мне надо адаптировать  –  в топку и целиком.
 | 
 
 
  |   |   | 
Нууу, брат ты погнал, "Я уберу virtual в определении...  21.12.07 21:05  
 Автор: + <Mikhail> Статус: Elderman
 | 
 
> > вот так было бы правильней, используя > наследственность, ну > > естественно надо сделать private copy constructor и > > assignment operator. > > Вообче то ето школьная задачка, не так ли?  >  > Так :(... Я бы даже сказал детсадовская... Но тем неменее > могут быть и  шероховатости! >  > > class ITopic > > { > > public: > >	 virtual ~ITopic(){}; > >	 virtual _bstr_t GetValue() = 0; > > }; >  > Я уберу  virtual в определении деструктора ~ITopic.  Нууу, брат ты погнал, "Я уберу 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 не будет фрагментации > памяти. 
 Фрагментация будет по любому, если ты конечно не преаллоцируешь память для твоих Topic objects
 
 >  >  > >	 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. Даже если и не 
 поетому smart poiter и нужен
 
 > придавать значения  такой “рассинхронизации” (не гут), то > некоторые обновления и вовсе могут быть потеряны, т.к. по > выходу из  SomeObject::RefreshData  вектор CtopicVector > очищается. Поэтому необходимы отдельные методы > Lock()/Unlock(). Другая причина в том, что вызов > EnterCriticalSection – это не бесплатный вызов, даже если > объект не блокируется (если критическая секция не занята, > то на EnterCriticalSection может приходится ~ 100 тактов > ЦПУ). Ну а если, уж объект синхнонизации блокирован, то > дело совсем плохо. Поэтому  нельзя, на мой взгляд, > использовать такие Lock-и внутри циклов. Надо делать это > снаружи циклов. > И последняя шероховатость, которая не относится к > эффективности, но к элегантности: > и у Вас и у Нас  есть по-существу GetUnsafeTopic, как Вы > справедливо заметили . Ключевое слово здесь Unsafe, и я не > знаю что с этим делать. Похоже этой неэлегантной > шероховатости не избежать, так чтоб не родить другую > неэлегантность. >  
 поетому smart poiter и нужен
 
 
 > В целом я очень признателен Вам, даже несмотря на > шероховатости обсуждаемых решений. Это придало мне > уверенности, что тот код (не мой), который мне надо > адаптировать  –  в топку и целиком. 
 показал бы ТОТ код , который "не нужен"
 | 
 
 
  |   |   |   | 
Согласен, что погнал, но не только здесь  23.12.07 00:22  
 Автор: void <Grebnev Valery> Статус: Elderman
 | 
 
> > Я уберу  virtual в определении деструктора ~ITopic.  > Нууу, брат ты погнал, "Я уберу virtual в определении > деструктора ~ITopic" > сотри немедленно и считай что я не слышал :). 
 Я попробовал встоить код в существующий солюшен... Не работает. 
 
 > показал бы ТОТ код , который "не нужен" 
 Не могу. То собственность комании. Там активно темплейты используются.
 | 
 
 
  |   |   | 
А вот ещё мелькнуло... Может вообще не освобождать вектор в...  21.12.07 05:11  
 Автор: void <Grebnev Valery> Статус: Elderman
 | 
 
А вот ещё мелькнуло... Может вообще не освобождать вектор в методе RefreshData. Тогда, можно будет использовать map, и дополнительно устанавливать флаг = 1 если данные были обновлены клиентом (флаг = 1 устанавливается клиентом), и сбрасывать флаг в 0 в методе RefreshData.
 Единственный недостаток пока вижу, это то, что если клиент перестал обновлять топик, (отсоединился), то соответствующуб ссылку надо удалить. Впрочем, это будет случаться не очень часто.
 
 ???
 
 > 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(); > }
 | 
 
 
  
 
 | 
 |