¿Hay contenedores concurrentes en C++11?


En particular, estoy buscando una cola de bloqueo. ¿Existe tal cosa en C++11? Si no, ¿cuáles son mis otras opciones? Realmente no quiero bajar al nivel de hilo yo mismo más. Demasiado propenso a errores.

Author: fredoverflow, 2011-10-19

5 answers

Según Diego Dagum del equipo de Visual C++ de Microsoft :

Una pregunta recurrente (bueno, una de las muchas) es sobre los contenedores STL y si son hilo seguro.

Tomando las palabras de Stephan aquí, la realidad es que no son, no como un bug pero como una característica: tener cada función miembro de cada STL la adquisición de un contenedor interno aniquilaría el rendimiento. Como una biblioteca de propósito general, altamente reutilizable, en realidad no proporcionar corrección ya sea: el nivel correcto para colocar bloqueos es determinado por lo que el programa está haciendo. En ese sentido, el individuo las funciones miembro no tienden a ser tan correctas.

La Biblioteca de Patrones Paralelos (PPL) incluye varios contenedores que proporcionan acceso seguro a sus elementos:

  • La Clase concurrent_vector es una clase contenedor de secuencia que permite el acceso aleatorio a cualquier elemento. Habilita el anexado seguro para la concurrencia, acceso a elementos, acceso iterador y operaciones de recorrido iterador.
  • La Clase concurrent_queue es una clase contenedor de secuencia que permite el acceso primero en entrar, primero en salir a sus elementos. Habilita un conjunto limitado de operaciones seguras de concurrencia, como push y try_pop, por nombrar algunas.

Algunas muestras aquí.

También interesante: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html.

 35
Author: Lior Kogan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-19 07:05:36

C++11 no proporciona contenedores concurrentes por sí mismo. Sin embargo, hay opciones de biblioteca. Además de la PPL ya mencionada, no se olvide de la biblioteca Intel TBB.

Tiene un concurrente queue, hash_map, set and vector implementation. Pero no solo es una biblioteca de contenedores segura para subprocesos, sino que también viene con una versión paralela de algoritmos estándar (for-loop, reduce, sort,...).

Sitio web de Intel TBB

 9
Author: Lars K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-28 13:38:42

Me sorprende que nadie haya mencionado moodycamel::ConcurrentQueue. Hemos estado usando durante bastante tiempo y funciona muy bien. Es específico que su implementación está libre de bloqueos, lo que inmediatamente trae una velocidad enorme. Otras razones para usarlo (citando del sitio oficial):

No hay muchas colas completas sin bloqueos para C++. Impulsar tiene uno, pero está limitado a objetos con operadores de asignación triviales y destructores triviales, por ejemplo. La cola TBB de Intel no libre de bloqueo, y requiere constructores triviales también. Hay muchos trabajos académicos que implementan colas sin bloqueos en C++, pero utilizables el código fuente es difícil de encontrar,y las pruebas aún más.

Algunos puntos de referencia y comparaciones están disponibles aquí, aquí y aquí.

 3
Author: Miljen Mikic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-06 14:03:45

Las interfaces de los contenedores simplemente no se han diseñado con este objetivo. Para las interfaces que utilizan, un bloqueo visible para el cliente es realmente la única manera de lograr esto al tiempo que garantiza la corrección y el comportamiento predecible. También sería terriblemente ineficiente porque el número de adquisiciones sería muy alto (en relación con una buena implementación).

Solución 1

Pasar por valor (cuando proceda).

Solución 2

Cree una colección de implementaciones simples atornilladas que puede usar para pasar contenedores mientras mantiene un bloqueo de alcance (considéralo 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;
};

Luego, el llamante empareja el bloqueo con la colección, y luego actualiza sus interfaces para usar (pasar) el tipo de contenedor cuando corresponda. Es sólo la extensión de clase de un pobre hombre.

Este contenedor bloqueado es un ejemplo simple, y hay algunas otras variantes. Esta es la ruta que elegí porque realmente le permite utilizar el nivel de granularidad que es ideal para su programa, a pesar de que no es tan transparente (sintácticamente) como los métodos bloqueados. También es relativamente fácil adaptar los programas existentes. Al menos se comporta de una manera predecible, a diferencia de las colecciones con cerraduras internas.

Otra variante sería:

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;

...donde podría utilizarse un tipo similar a t_locked_collection para exponer la colección subyacente. No quiero dar a entender que ese enfoque es infalible, solo tonto resistente.

 1
Author: justin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-19 08:04:40

Mi versión de un mapa no ordenado concurrente concurrencia de espacios de nombres {

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);
}
};
}
 0
Author: Asif Bahrainwala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-25 11:18:40