Вопрос

В частности, я ищу блокирующую очередь. Есть ли такая вещь в C ++ 11? Если нет, то каковы мои другие варианты? Я действительно не хочу больше идти на уровень нити сам. Слишком ошибочно.

Это было полезно?

Решение

По словам Диего Дагума из команды Microsoft Visual C ++:

Рецидивый вопрос (ну, один из многих) касается контейнеров STL и безопасных ли они.

Принимая слова Стефана здесь, реальность такова, что это не так, как ошибка, а как функция: каждая функция члена из каждого контейнера STL, приобретающий внутренний блокировку, будет уничтожать производительность. Как общая цель, библиотека с широким использованием, она на самом деле также не обеспечит правильность: правильный уровень размещения замков определяется тем, что делает программа. В этом смысле функции отдельных членов не имеют тенденции к такому правильному уровню.

Библиотека параллельных узоров (PPL) включает в себя несколько контейнеров, которые обеспечивают безопасное доступ к своим элементам:

  • А CONDURRENT_VECTOR CLASS это класс контейнеров последовательности, который допускает случайный доступ к любому элементу. Это позволяет приложение, безопасное для параллели, доступ к элементу, доступ к итераторному доступу и операции обхода итератора.
  • А CONDURRENT_QUEUE Class это последовательный класс контейнеров, который допускает первое, первое, доступ к своим элементам. Это позволяет ограничить набор операций, безопасных для параллелизации, таких как push и try_pop, назвать несколько.

Некоторые образцы здесь.

Также интересно: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html.

Другие советы

C ++ 11 не обеспечивает одновременные контейнеры сам по себе. Тем не менее, есть варианты библиотеки. Помимо уже упомянутого PPL, не забудьте библиотеку Intel TBB.

У него есть одновременный queue, hash_map, set а также vector реализация. Но это не только безопасная библиотека контейнеров, она также поставляется с параллельной версией стандартных алгоритмов (для петли, уменьшить, сортировать, ...).

Intel TBB Веб -сайт

Я удивлен, что никто не упомянул MoodyCamel :: CONCURRENTQUEUE. Анкет Мы использовали его в течение довольно долгого времени, и он работает очень хорошо. В зависимости от того, что его реализация не имеет блокировки, что сразу же приносит огромную скорость. Другие причины для его использования (цитирование с официального сайта):

Не так много полноценных очередей без блокировки для C ++. У Boost есть один, но он ограничен объектами с тривиальными операторами назначения и тривиальными деструкторами, например. Очередь Intel TBB не является без блокировки, и требует тривиальных конструкторов. Есть много академических документов, которые внедряют очередь без блокировки в C ++, но используемый исходный код трудно найти, и тестирует еще больше.

Доступны некоторые тесты и сравнения здесь, здесь а также здесь.

Интерфейсы контейнеров просто не были разработаны с этой целью. Для интерфейсов, которые они используют, блокировка, видимая для клиента, действительно является единственным способом, которым вы можете достичь этого, гарантируя правильность и предсказуемое поведение. Это также было бы ужасно неэффективно, потому что количество приобретений было бы очень высоким (относительно хорошей реализации).

Решение 1

Пройти по значению (где применимо).

Решение 2

Создайте коллекцию простых реализаций Bolt-on, которые вы можете использовать для прохождения контейнеров при хранении блокировки прицела (учитывайте это Pseudo C ++):

template <typename TCollection>
class t_locked_collection {
public:
    t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() {
    }

    TCollection& collection;
    // your convenience stuff
private:
    t_scope_lock d_lock;
    t_nocopy d_nocopy;
};

Затем вызывающий абонент соединяет блокировку с коллекцией, а затем вы обновляете свои интерфейсы, чтобы использовать (пройти) тип контейнера, где это необходимо. Это просто расширение класса бедного человека.

Этот заблокированный контейнер является одним из простых примеров, и есть несколько других вариантов. Это маршрут, который я выбрал, потому что он действительно позволяет вам использовать уровень детализации, который идеально подходит для вашей программы, даже если он не такой прозрачный (синтаксически), как заблокированные методы. Также относительно легко адаптировать существующие программы. По крайней мере, это ведет себя предсказуемо, в отличие от коллекций с внутренними замками.

Другим вариантом будет:

template <typename TCollection>
class t_lockable_collection {
public:
// ...
private:
    TCollection d_collection;
    t_mutex d_mutex;
};

// example:
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector;

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

Моя версия одновременной переупорядоченной карты параллелизма имен имен {

template<typename T,typename T1>
class unordered_bucket: private std::unordered_map<T,T1>
{
mutable std::recursive_mutex m_mutex;

public:
T1 &operator [](T a)
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    return std::unordered_map<T,T1>::operator [](a);
}

size_t size() const noexcept {
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    return  std::unordered_map<T,T1>::size();
}

vector<pair<T,T1>> toVector() const
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);

    vector<pair<T,T1>> ret;
    for(const pair<T,T1> &p:*this)
    {
        ret.push_back(p);
    }
    return ret;
}

bool find(const T &t) const
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    if(this->std::unordered_map<T,T1>::find(t) == this->end())
        return false;  //not found
    return true;
}
void erase()
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    this->unordered_map<T,T1>::erase(this->begin(),this->end());
}
void erase(const T &t)
{
    std::lock_guard<std::recursive_mutex> l(m_mutex);
    this->unordered_map<T,T1>::erase(t);
}
};

#define BUCKETCOUNT 10
template<typename T,typename T1>
class ConcurrentMap
{
std::vector<unordered_bucket<T,T1>> m_v;
public:
ConcurrentMap():m_v(BUCKETCOUNT){}   //using 10 buckets

T1 &operator [](T a)
{
    std::hash<T> h;
    return m_v[h(a)%BUCKETCOUNT][a];
}

size_t size() const noexcept {
    size_t cnt=0;

    for(const unordered_bucket<T,T1> &ub:m_v)
        cnt=cnt+ub.size();

    return  cnt;
}

vector<pair<T,T1>> toVector() const
{
    vector<pair<T,T1>> ret;
    for(const unordered_bucket<T,T1> &u:m_v)
    {
        const vector<pair<T,T1>> &data=u.toVector();
        ret.insert(ret.end(),data.begin(),data.end());
    }
    return ret;
}

bool find(const T &t) const
{
    for(const unordered_bucket<T,T1> &u:m_v)
        if(true == u.find(t))
            return true;
    return false;
}
void erase()
{
    for(unordered_bucket<T,T1> &u:m_v)
        u.erase();
}
void erase(const T &t)
{
    std::hash<T> h;
    unordered_bucket<T,T1> &ub = m_v[h(t)%BUCKETCOUNT];
    ub.erase(t);
}
};
}

В C ++ 11 нет одновременных контейнеров.

Но в следующем классе заголовка содержится одновременная очередь, стеки и приоритетные контейнеры с использованием std :: deque.

Блокировка это класс Safe Collection Clate Cleafle C ++ 11, который моделируется после класса блокировки .NET.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top