Контейнеры с повышенной потокобезопасностью
От: Garrett Россия  
Дата: 02.11.06 14:20
Оценка:
Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.

Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.
Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться
в борьбе со здравым смыслом победа будет за нами!
Re: Контейнеры с повышенной потокобезопасностью
От: Какая разница Украина  
Дата: 02.11.06 15:02
Оценка: +2
Здравствуйте, Garrett, Вы писали:

G>Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.


G>Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.

G>Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться

Не мучайся этой блажью
Синхронизируй доступ к контейнеру извне

Пример
пусть у тебя есть враппер над vector в котором во всех методах вставлена синхронизация например по критической секции
И что ты думаеш это тебя спасло и ничего делать больше не надо
Ошибаешся
Где гарантия что после строки 1 не выполнится код Thread2() и не удалит весь контейнер
Вывод всю покобезопасность надо реализовать извне а не внутри контейнеров

std::vector <int> C;

UINT Thread1()
{
    ...
    size_t = C.size();
    if (t == 0)
    {
         iterator it = C.begin();  // 1
                                  
         *t = 666;                // 2
    }
    ....
}

UINT Thread2()
{
   ....
   C.erase(C.begin(), C.end());
   ....
}
!0xDEAD
Re[2]: Контейнеры с повышенной потокобезопасностью
От: Какая разница Украина  
Дата: 02.11.06 15:08
Оценка:
КР>Здравствуйте, Garrett, Вы писали:

G>>Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.


G>>Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.

G>>Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться

КР>Не мучайся этой блажью

КР>Синхронизируй доступ к контейнеру извне

КР>Пример

КР>пусть у тебя есть враппер над vector в котором во всех методах вставлена синхронизация например по критической секции
КР>И что ты думаеш это тебя спасло и ничего делать больше не надо
КР>Ошибаешся
КР>Где гарантия что после строки 1 не выполнится код Thread2() и не удалит весь контейнер
КР>Вывод всю покобезопасность надо реализовать извне а не внутри контейнеров


маленькие коррекции а то быстро писал и наделал ошибок
КР>
КР>std::vector <int> C;

КР>UINT Thread1()
КР>{
КР>    ...
КР>    size_t = C.size();
КР>    if (t != 0)
КР>    {
КР>         iterator it = C.begin();  // 1
                                  
КР>         *it = 666;                // 2
КР>    }
КР>    ....
КР>}

КР>UINT Thread2()
КР>{
КР>   ....
КР>   C.erase(C.begin(), C.end());
КР>   ....
КР>}
КР>
!0xDEAD
Re[2]: Контейнеры с повышенной потокобезопасностью
От: Garrett Россия  
Дата: 02.11.06 15:14
Оценка:
Здравствуйте, Какая разница, Вы писали:

КР>Здравствуйте, Garrett, Вы писали:


G>>Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.


G>>Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.

G>>Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться

КР>Не мучайся этой блажью

КР>Синхронизируй доступ к контейнеру извне

Не в моем случае.
Ситуация — два потока. Первый — берет данные из сети и набивает в контейнер (сейчас использую std::hash_map ), его надолго тормозить нельзя. Второй поток занимается медленной обработкой данных, которые лежат в контейнере — поиском и удалением. Его можно и притормозить, если необходимо.
Сейчас вот придумал такую конструкцию — контейнер состоит из входа и базы. На входе — два мелких контейнера, в один из них шустрый поток пихает данные. Как только контейнер заполняется — для напихивания передается второй контейнер, а содержимое первого переливается в основной. Медленный поток
шарится по базовому контейнеру и оперирует с ним. Соответсвенно синхронизировать нужно только момент переключения.
Но уж больно монструозная конструкция получается.

КР>Пример

КР>пусть у тебя есть враппер над vector в котором во всех методах вставлена синхронизация например по критической секции
КР>И что ты думаеш это тебя спасло и ничего делать больше не надо
КР>Ошибаешся
КР>Где гарантия что после строки 1 не выполнится код Thread2() и не удалит весь контейнер
КР>Вывод всю покобезопасность надо реализовать извне а не внутри контейнеров

Ну, хотелось бы, чтобы по-настоящему потокобезопасный вектор при вызове erase удалил все элементы кроме того, которых захапал it, и ждал, пока it его не освободит. После чего удалил и его.

КР>
КР>std::vector <int> C;

КР>UINT Thread1()
КР>{
КР>    ...
КР>    size_t = C.size();
КР>    if (t == 0)
КР>    {
КР>         iterator it = C.begin();  // 1
                                  
КР>         *t = 666;                // 2
КР>    }
КР>    ....
КР>}

КР>UINT Thread2()
КР>{
КР>   ....
КР>   C.erase(C.begin(), C.end());
КР>   ....
КР>}
КР>
в борьбе со здравым смыслом победа будет за нами!
Re: Контейнеры с повышенной потокобезопасностью
От: Alexey Frolov Беларусь  
Дата: 02.11.06 15:53
Оценка:
Здравствуйте, Garrett, Вы писали:

G>Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.


G>Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.

G>Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться

Есть такое чудо от microsoft, но насколько оно подойдет решать вам
Interlocked Singly Linked Lists
Re[3]: Контейнеры с повышенной потокобезопасностью
От: AndrewJD США  
Дата: 02.11.06 17:04
Оценка:
Здравствуйте, Garrett, Вы писали:

G>Ситуация — два потока. Первый — берет данные из сети и набивает в контейнер (сейчас использую std::hash_map ), его надолго тормозить нельзя. Второй поток занимается медленной обработкой данных, которые лежат в контейнере — поиском и удалением. Его можно и притормозить, если необходимо.

G>Сейчас вот придумал такую конструкцию — контейнер состоит из входа и базы. На входе — два мелких контейнера, в один из них шустрый поток пихает данные. Как только контейнер заполняется — для напихивания передается второй контейнер, а содержимое первого переливается в основной. Медленный поток
G> шарится по базовому контейнеру и оперирует с ним. Соответсвенно синхронизировать нужно только момент переключения.
G>Но уж больно монструозная конструкция получается.

Нормально получается. Лочишь оба контейнера, делаешь hash_map::swap и отпускаешь. Все просто и минимальные потери на локе.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[2]: Контейнеры с повышенной потокобезопасностью
От: Аноним  
Дата: 02.11.06 17:38
Оценка:
Здравствуйте, Alexey Frolov, Вы писали:

AF>Здравствуйте, Garrett, Вы писали:


G>>Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.


G>>Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.

G>>Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться

AF>Есть такое чудо от microsoft, но насколько оно подойдет решать вам

AF>Interlocked Singly Linked Lists

Самое смешное, что даже доступ к этому чуду скорее всего надо будет синхронизировать
Re[4]: Контейнеры с повышенной потокобезопасностью
От: Garrett Россия  
Дата: 03.11.06 09:23
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>Нормально получается. Лочишь оба контейнера, делаешь hash_map::swap и отпускаешь. Все просто и минимальные потери на локе.


Так в лоб, пожалуй, делать не буду. Сделаю — кину сюда на обсуждение
в борьбе со здравым смыслом победа будет за нами!
Re: HashSet для двух потоков
От: Garrett Россия  
Дата: 03.11.06 14:01
Оценка:
Вот, велосипед. Может кому пригодится.
Предполагается, что быстрый поток только кладет данные в контейнер, а медленнй занимается поиском и удалением
// Пул входных контейнеров
template < class StoredType >
class C_InputPool
{
public:
  typedef std::list< StoredType > TContainer;
  typedef std::vector< TContainer > TInputPool; 
private:
  TInputPool pool;
  typename TInputPool::iterator input;
  CRITICAL_SECTION InputProtector;
public:
  C_InputPool(size_t PoolSize = 2);
  ~C_InputPool(void);
public:
  void Insert(StoredType value);
  TContainer* Swap(void);
  size_t Size();
};

// Хеш-мап, синхронизированный по вставке, удалению и поиску
template < class StoredType >
class C_SyncHashSet
{
public:
  typedef C_InputPool<StoredType> TInputPool;
  typedef stdext::hash_set<StoredType> TBaseContainer;
private:
  TInputPool InputPool;
  TBaseContainer base;
public:
  C_SyncHashSet(void);
  ~C_SyncHashSet(void);
public:
  void Swap(void);
  void Insert(StoredType value);
  size_t Count(StoredType value);
  size_t Size(void);
  void Erase(StoredType value);
};


//- C_SyncHashSet ----------------------------------------------------------------------
template < typename StoredType >
C_SyncHashSet<StoredType>::C_SyncHashSet():base(),InputPool()
{
}

template < typename StoredType >
C_SyncHashSet<StoredType>::~C_SyncHashSet()
{
}

template<class StoredType>
void C_SyncHashSet<StoredType>::Swap(void)
{
  TInputPool::TContainer* swapped = InputPool.Swap();
  if( swapped->empty()) return;

  base.insert(swapped->begin(), swapped->end());
  swapped->clear();
}
 
template<class StoredType>
void C_SyncHashSet<StoredType>::Insert(StoredType value)
{
  InputPool.Insert(value);    
}

template < class StoredType >
size_t C_SyncHashSet<StoredType>::Count(StoredType value)
{
  Swap();
  return base.count(value);
}

template < class StoredType >
size_t C_SyncHashSet<StoredType>::Size(void)
{
  return base.size() + InputPool.Size();
}

template<class StoredType>
void C_SyncHashSet<StoredType>::Erase(StoredType value)
{
  Swap();
  base.erase(value);
}

//- C_InputPool ------------------------------------------------------------------------

template < typename StoredType >
C_InputPool<StoredType>::C_InputPool(size_t PoolSize) :pool(PoolSize)
{
  ASSERT( PoolSize > 1 );
  input = pool.begin();
  InitializeCriticalSection(&InputProtector);
}

template < typename StoredType >
C_InputPool<StoredType>::~C_InputPool(void)
{
  DeleteCriticalSection(&InputProtector);
}

template < class StoredType >
void C_InputPool<StoredType>::Insert(StoredType value)
{
  EnterCriticalSection(&InputProtector);
  input->push_back(value);
  LeaveCriticalSection(&InputProtector);
}

template < class StoredType >
typename C_InputPool<StoredType>::TContainer* C_InputPool<StoredType>::Swap(void)
{
  TContainer * ret = &(*input);
  EnterCriticalSection(&InputProtector);

  input++;
  if(input == pool.end() ) input = pool.begin();

  LeaveCriticalSection(&InputProtector);
  return ret;
}

template < class StoredType >
size_t C_InputPool<StoredType>::Size()
{
  size_t ret = 0;
  TInputPool::iterator it;
  for( it = pool.begin(); it != pool.end(); it++)
  {
    if(it == input) EnterCriticalSection(&InputProtector);
    ret += it->size();
    if(it == input) LeaveCriticalSection(&InputProtector);
  }
  return ret;
}
в борьбе со здравым смыслом победа будет за нами!
Re[2]: HashSet для двух потоков
От: Аноним  
Дата: 03.11.06 14:41
Оценка: +1
Здравствуйте, Garrett, Вы писали:

G>Вот, велосипед. Может кому пригодится.

В таком виде использовать не советую.

Вот такие штуки опасны дедлоками, если вдруг между Enter и Leave выскочит исключение.
EnterCriticalSection(&InputProtector);
// bla bla bla 
LeaveCriticalSection(&InputProtector);

Используй технику scope quard

Ну а вообще ты не очень аккуратно делаешь блокировки.
Re[3]: HashSet для двух потоков
От: Garrett Россия  
Дата: 03.11.06 15:00
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Garrett, Вы писали:


G>>Вот, велосипед. Может кому пригодится.

А>В таком виде использовать не советую.

А>Вот такие штуки опасны дедлоками, если вдруг между Enter и Leave выскочит исключение.

А>
А>EnterCriticalSection(&InputProtector);
А>// bla bla bla 
А>LeaveCriticalSection(&InputProtector);
А>


Ну, в этом случае исключений быть не должно. Там же простые операции с заведомо валидным итератором. Конечно, это если пользоваться им как предполагается


А>Используй технику scope quard

А>Ну а вообще ты не очень аккуратно делаешь блокировки.

А как надо?
в борьбе со здравым смыслом победа будет за нами!
Re[2]: HashSet для двух потоков
От: rm822 Россия  
Дата: 03.11.06 17:19
Оценка:
Здравствуйте, Garrett, Вы писали:

G>Вот, велосипед. Может кому пригодится.

G>Предполагается, что быстрый поток только кладет данные в контейнер, а медленнй занимается поиском и удалением
G>
G>  typedef std::list< StoredType > TContainer;
G>  typedef std::vector< TContainer > TInputPool; 
G>

ИМХО это не самый лучший вариант,
каждый insert в list это new, а new это лишняя синхронизация и просто лишняя работа
лучше наоборот
  typedef std::vector< StoredType > TContainer;
  typedef std::list< TContainer > TInputPool;

+ вызвать reserve в векторах чтобы не было realloc

Ну и я бы не так сделал, синхронизация на class C_InputPool ИМХО опять же не нужна вовсе,
лучше бы он был локальным для потока, в конце концов swap делать это не мешает.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Контейнеры с повышенной потокобезопасностью
От: MaximE Великобритания  
Дата: 06.11.06 11:13
Оценка: 1 (1)
Garrett wrote:

> Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб

> функциональности и универсальности.

Далается так:

template<class Container>
struct thread_safe_container
{
     boost::mutex m;
     Container c;
};


В остальных 90% случаев тебе нужно синхронизировать доступ более чем к одной
переменной (например список + счетчик), поэтому подход использовать один mutex
на одну переменную становится непрактичным, много удобнее использовать один
мьютекс на несколько логически связанных переменных.

По этой причине thread-safe контейнеры часто есть не очень удачная идея.

--
Maxim Yegorushkin

No Microsoft product was used in any way to write or send this text.
If you use a Microsoft product to read it, you're doing so at your own risk
Posted via RSDN NNTP Server 2.0
Re[3]: Контейнеры с повышенной потокобезопасностью
От: Alexey Frolov Беларусь  
Дата: 06.11.06 11:41
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Alexey Frolov, Вы писали:


AF>>Здравствуйте, Garrett, Вы писали:


G>>>Ищу библиотеку с контейнерами, в которых потокобезопасность получше чем в STL.


G>>>Посоветуйте кто какие знает, даже если потокобезопасность идет в ущерб функциональности и универсальности.

G>>>Уж больно свои делать неохота. Fifo-контейнер нашел, но его мало, а писать самому — боюсь закопаться

AF>>Есть такое чудо от microsoft, но насколько оно подойдет решать вам

AF>>Interlocked Singly Linked Lists

А>Самое смешное, что даже доступ к этому чуду скорее всего надо будет синхронизировать


все уже украдено до нас (с)
Ничего не надо синхронизировать дополнительно, другое дело, что этот контейнер вряд ли подойдет автору топика, судя по требуемой функциональности. А так я бы назвал его лучшим
Re[3]: HashSet для двух потоков
От: Garrett Россия  
Дата: 07.11.06 07:16
Оценка:
Здравствуйте, rm822, Вы писали:

R>Здравствуйте, Garrett, Вы писали:


G>>Вот, велосипед. Может кому пригодится.

G>>Предполагается, что быстрый поток только кладет данные в контейнер, а медленнй занимается поиском и удалением
G>>
G>>  typedef std::list< StoredType > TContainer;
G>>  typedef std::vector< TContainer > TInputPool; 
G>>

R>ИМХО это не самый лучший вариант,
R>каждый insert в list это new, а new это лишняя синхронизация и просто лишняя работа
R>лучше наоборот
R>
R>  typedef std::vector< StoredType > TContainer;
R>  typedef std::list< TContainer > TInputPool; 
R>

R>+ вызвать reserve в векторах чтобы не было realloc

можно и так, но тогда придется либо делать дополнительные reserve при заполнении вектора, либо переключать и переливать в базовый контейнер в быстром потоке.


R>Ну и я бы не так сделал, синхронизация на class C_InputPool ИМХО опять же не нужна вовсе,

R>лучше бы он был локальным для потока, в конце концов swap делать это не мешает.
Не понял... что значит локальным для потока? Поподробнее можно?
в борьбе со здравым смыслом победа будет за нами!
Re[4]: HashSet для двух потоков
От: rm822 Россия  
Дата: 07.11.06 13:55
Оценка:
Здравствуйте, Garrett, Вы писали:

G>Здравствуйте, rm822, Вы писали:


R>>Здравствуйте, Garrett, Вы писали:


G>>>Вот, велосипед. Может кому пригодится.

G>>>Предполагается, что быстрый поток только кладет данные в контейнер, а медленнй занимается поиском и удалением
G>>>
G>>>  typedef std::list< StoredType > TContainer;
G>>>  typedef std::vector< TContainer > TInputPool; 
G>>>

R>>ИМХО это не самый лучший вариант,
R>>каждый insert в list это new, а new это лишняя синхронизация и просто лишняя работа
R>>лучше наоборот
R>>
R>>  typedef std::vector< StoredType > TContainer;
R>>  typedef std::list< TContainer > TInputPool; 
R>>

R>>+ вызвать reserve в векторах чтобы не было realloc

G>можно и так, но тогда придется либо делать дополнительные reserve при заполнении вектора, либо переключать и переливать в базовый контейнер в быстром потоке.



R>>Ну и я бы не так сделал, синхронизация на class C_InputPool ИМХО опять же не нужна вовсе,

R>>лучше бы он был локальным для потока, в конце концов swap делать это не мешает.
G>Не понял... что значит локальным для потока? Поподробнее можно?

в таком вот аспекте
C_InputPool& C_InputPool::ThreadInstance()
{
static __declspec( thread ) C_InputPool instance;
return instance;
}

или в таком
void SomeFoo()
{
C_InputPool<....> pool;
.....
}

но не в таком
C_InputPool<....> g_pool;
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: HashSet для двух потоков
От: Garrett Россия  
Дата: 07.11.06 16:02
Оценка:
G>>Не понял... что значит локальным для потока? Поподробнее можно?


R>в таком вот аспекте
R>C_InputPool& C_InputPool::ThreadInstance()
R>{
R>  static __declspec( thread ) C_InputPool instance;
R>    return instance;
R>}

R>или в таком
R>void SomeFoo()
R>{
R>   C_InputPool<....> pool;
R>     .....
R>}
R>но не в таком
R>C_InputPool<....> g_pool;


Не догнал идею. :xz:
По-любому экземпляр C_InputPool должен существовать между потоками. Потому как потоку-поставщику некогда делать слив данных из пула в базу.
Вот если потоков несколько, то можно каждому выделить по экземпляру пула... Можно еще заставить APC делать переключение, но до этого уже вряд ли руки дойдут.
в борьбе со здравым смыслом победа будет за нами!
Re[6]: HashSet для двух потоков
От: rm822 Россия  
Дата: 07.11.06 16:07
Оценка:
Здравствуйте, Garrett, Вы писали:


G>По-любому экземпляр C_InputPool должен существовать между потоками. Потому как потоку-поставщику некогда делать слив данных из пула в базу.

Ну если поток один зачем ему существовать между? И тем более тогда зачем ему внутри CriticalSection?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[7]: HashSet для двух потоков
От: Garrett Россия  
Дата: 08.11.06 07:10
Оценка:
Здравствуйте, rm822, Вы писали:

R>Здравствуйте, Garrett, Вы писали:



G>>По-любому экземпляр C_InputPool должен существовать между потоками. Потому как потоку-поставщику некогда делать слив данных из пула в базу.

R>Ну если поток один зачем ему существовать между? :) И тем более тогда зачем ему внутри CriticalSection?

Потока как минимум два. Один кладет данные, другой сливает в базу, ищет и удаляет.
в борьбе со здравым смыслом победа будет за нами!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.