Реализация блокировки на C++

Извините, что вопрос этой проблемы может быть немного расплывчатым. Я пытаюсь перенести этот код ObjectPool с С# на С++, но, похоже, есть некоторые части, в которых я не знаю, как мне действовать. Коды следующие:

using System;

namespace FastRank
{
    public class ObjectPool<T> where T : class, new()
    {
        private int _count;

        private T[] _pool;

        public ObjectPool(int initSize)
        {
            _pool = new T[initSize];
        }

        public T Get()
        {
            lock (_pool)
            {
                if (_count > 0)
                {
                    --_count;
                    T item = _pool[_count];
                    _pool[_count] = null;
                    return item;
                }
            }
            return new T();
        }

        public void Return(T item)
        {
            lock (_pool)
            {
                if (_count == _pool.Length)
                    Array.Resize(ref _pool, _pool.Length*2 + 1);
                _pool[_count++] = item;
            }
        }
    }
}

Мои вопросы:

1) Как мне реализовать это ограничение на общий параметр T в С++? (класс, новый())

2) Есть ли простой способ реализовать блокировку мьютекса?

3) Будет ли более эффективно определять _pool как вектор вместо T[] в C++?

edit -> Реализовано что-то вроде:

#include "object_pool.h"

#include <boost/thread.hpp>
#include <vector>
using namespace std;

template <class T>
ObjectPool<T>::ObjectPool(int init_size) {
  pool_.reserve(init_size);
}

template <class T>
T ObjectPool<T>::Get() {
  boost::lock_guard<boost::mutex> lock(guard_);
  int sz = (int) pool_.size();
  if (sz == 0) {
    throw "Object pool size is now zero.";
  }
  else {
    T item = pool_[sz-1];
    pool_.pop_back();
    return item;
  } 
}

template <class T>
void ObjectPool<T>::Return(T item) {
  boost::lock_guard<boost::mutex> lock(guard_);
  pool_.push_back(item);
}

Интересно, есть ли проблемы с этим кодом...


person derekhh    schedule 17.11.2011    source источник
comment
Должен ли он быть портативным или на одной конкретной платформе?   -  person Jon Cage    schedule 18.11.2011
comment
@JonCage: Было бы лучше, если бы он был портативным. Я бы предпочел использовать стандартный С++ и пакет boost, если это возможно.   -  person derekhh    schedule 18.11.2011
comment
Это должно быть прямолинейно, чтобы соорудить что-то из <mutex>; класс должен получить объект мьютекса-члена, и вы блокируете его во время операций Get и Return.   -  person Kerrek SB    schedule 18.11.2011
comment
Чтобы продолжить вопрос Джона, если это специфично для MS, это родной С++ или С++/CLI?   -  person harlam357    schedule 18.11.2011
comment
@harlam357: На самом деле я пытаюсь портировать его на Linux...   -  person derekhh    schedule 18.11.2011


Ответы (3)


Вот наивный фрагмент, иллюстрирующий один из возможных подходов:

#include <mutex>

template <typename T>
class SyncStack
{
   T         * m_data;
   std::size_t m_size;
   std::size_t m_count;
   std::mutex  m_lock;

public:
  T get()
  {
    std::lock_guard<std::mutex> lock(m_lock);

    if (m_count == 0) { throw UnderrunException; }

    --m_count;
    T x(m_data[m_count]);
    m_data[m_count].~T();

    return x;
  }

  void put(T x)
  {
    std::lock_guard<std::mutex> lock(m_lock);
    ::new (m_data + m_count) T(std::move(x));
    ++m_count;
  }
};

В этом примере предполагается, что m_data указывает на бесконечную память. Перераспределение немного сложнее и требует создания большого количества копий.

Более простым подходом было бы обернуть вашу синхронизированную структуру вокруг другого существующего стандартного контейнера, такого как std::vector<T>.

person Kerrek SB    schedule 17.11.2011
comment
Не могли бы вы изменить свой код с использования ‹mutex› на boost::mutex, если это возможно? Проблема в том, что я не нашел на их сайте ни одного рабочего руководства по boost::mutex... - person derekhh; 18.11.2011
comment
@derekhh: просто замените std::mutex на boost::mutex, std::lock_guard на boost::lock_guard и <mutex> на <boost/thread.hpp> :-) - person Kerrek SB; 18.11.2011
comment
Не могли бы вы помочь мне проверить, все ли в порядке с моим исправленным кодом...? :-) - person derekhh; 18.11.2011
comment
@derekhh: Выглядит хорошо! Я бы использовал back() вместо [sz-1]. Ну, по модулю ошибок и тонкостей, я бы сказал, что все идет в правильном направлении :-) - person Kerrek SB; 18.11.2011
comment
@derekhh: Потому что я владелец поста. Я всегда получаю уведомления о комментариях к моему собственному сообщению. - person Kerrek SB; 18.11.2011
comment
Ага! Еще один хитрый прием в stackexchange :-) На самом деле я пытался перезагрузить компьютер, чтобы посмотреть, что не так с моей конфигурацией... - person derekhh; 18.11.2011

1) Как мне реализовать это ограничение на общий параметр T в С++? (класс, новый())

В общем, не надо. Если он не соответствует ограничениям, он не сможет скомпилироваться. Достаточно просто. Есть хитрые способы улучшить сообщения об ошибках, но я забыл о них навскидку, потому что никогда не беспокоился.

2) Есть ли простой способ реализовать блокировку мьютекса?

Используйте boost::mutex.

3) Будет ли более эффективно определять _pool как вектор вместо T[] в C++?

Учитывая, что у вас не может быть локального T[] без размера, да. Используйте std::vector. (Вы можете иметь его в качестве параметра, но не в определении переменной.)

person Mooing Duck    schedule 17.11.2011

Вот как бы я это реализовал. Вы можете заменить tbb::concurrenct_queue на std::mutex защищенный std::queue, хотя это будет менее эффективно. С этой реализацией вам нужно беспокоиться о «возвращении» объектов обратно в пул, это обрабатывается автоматически.

#include <memory>
#include <tbb/concurrent_queue.h>

namespace FastRank
{
    template<typename T>
    class object_pool
    {
        typedef tbb::concurrent_bounded_queue<std::shared_ptr<T>> pool_t;       
        std::shared_ptr<pool_t> pool_;
    public:

        object_pool() : pool_(new pool_t())
        {
        }

        std::shared_ptr<T> get()
        {
            std::shared_ptr<T> ptr;
            if(!pool_.try_pop(ptr))
                ptr = std::make_shared<T>();

            auto pool = pool_;
            return std::shared_ptr<T>(ptr.get(), [pool, ptr](T*){pool->push(ptr);});
        }
    }
}

Без concurrent_queue

#include <memory>
#include <queue>
#include <boost/mutex.hpp>

namespace FastRank
{
    template<typename T>
    class object_pool
    {
        typedef std::pair<std::queue<std::shared_ptr<T>>, boost::mutex> pool_t;     
        std::shared_ptr<pool_t> pool_;
    public:

        object_pool() : pool_(new pool_t())
        {
        }

        std::shared_ptr<T> get()
        {
            std::shared_ptr<T> ptr;
            {
                boost::scoped_lock<boost::mutex> lock(pool_->second);
                if(!pool_->first.empty())
                {
                     ptr = std::move(pool->first.front());
                     pool->first.pop()
                }
            }

            if(!ptr)
                ptr = std::make_shared<T>();

            auto pool = pool_;
            return std::shared_ptr<T>(ptr.get(), [pool, ptr](T*)
            {
                boost::scoped_lock<boost::mutex> lock(pool->second);                    
                pool->push(ptr);
            });
        }
    }
}
person ronag    schedule 17.11.2011
comment
можно ли использовать только boost::mutex и std::vector? я не так хорошо знаком с пакетами TBB... - person derekhh; 18.11.2011
comment
@derekhh: я добавил пример с использованием boost::mutex. - person ronag; 18.11.2011
comment
Не могли бы вы помочь мне проверить, все ли в порядке с моим исправленным кодом...? - person derekhh; 18.11.2011