¿Es posible crear un vector atómico o una matriz en C++?

6 minutos de lectura

avatar de usuario de arco iris
arcoíris

Tengo un código que usa una matriz de int (int[]) en un hilo que se activa cada segundo.

yo suelo lock() de std::mutex para bloquear esta matriz en este hilo.

Sin embargo, me pregunto si hay una manera de crear una matriz atómica (o vector) para evitar el uso de un mutex. Intenté un par de formas, pero el compilador siempre se queja de alguna manera.

Sé que hay una forma de crear una matriz de átomos, pero no es lo mismo.

  • ¿Cuáles son las formas en que lo intentó y cómo exactamente se quejó el compilador?

    – 463035818_no_es_un_número

    6 de septiembre de 2017 a las 7:36


  • ¿Posible duplicado de matriz/vector/mapa/etc sin bloqueo (preferiblemente boost)?

    –Robinson

    6 de septiembre de 2017 a las 7:45

  • Por cierto, atomic no garantiza la ausencia de bloqueo, por lo que la implementación puede hacer algo similar a mutex.

    – Jarod42

    6 de septiembre de 2017 a las 7:45

  • @ tobi303 Traté de hacer algunas combinaciones como esta: std::atomic miArray = {0,0,0}; o así: std::atomic miArray; etcétera.

    – arcoíris

    6 de septiembre de 2017 a las 7:48

  • Hay algunas charlas muy buenas de CPPCON sobre programación sin bloqueo y medición de rendimiento. La respuesta corta es no se moleste a menos que la versión mutex no pueda cumplir con las expectativas de rendimiento de sus usuarios. Si ese es el caso, entonces con toda probabilidad es su diseño el que está mal. Un almacén atómico tarda ~10 veces más que uno no atómico. Si está haciendo una serie de tiendas, es mejor que mantenga un candado y las haga sin atómicos.

    -Richard Hodges

    6 de septiembre de 2017 a las 8:03

Martin Bonner apoya el avatar de usuario de Monica
Martin Bonner apoya a Mónica

En la práctica, a nivel de CPU, hay instrucciones que pueden actualizar atómicamente un inty un buen compilador los usará para std::atomic<int>. Por el contrario, no hay instrucciones que puedan actualizar atómicamente un vector de enteros (para cualquier arquitectura que conozca), por lo que tiene consiguió ser un mutex de algún tipo en alguna parte. También podrías dejar que sea tu mutex.


Para futuros lectores que aún no han escrito código con mutex:

No puedes crear un std::atomic de int[10], porque eso conduce a una función que devuelve una matriz, y no puede tenerlos. Lo que tu poder hacer, es tener un std::atomic<std::array<int,10>>

int main()
{
  std::atomic<std::array<int,10>> myArray;
}

Tenga en cuenta que el compilador/biblioteca creará un mutex debajo del capó para hacer esto atómico. Tenga en cuenta además que esto no hace lo que quiere. Le permite establecer el valor de toda la matriz de forma atómica.

Él no le permite leer toda la matriz, actualizar un elemento y volver a escribir toda la matriz de forma atómica.

Las lecturas y las escrituras serán individualmente atómicas, pero otro hilo puede interponerse entre la lectura y la escritura.

¡Necesitas el mutex!

  • podría valer la pena mencionar que esta matriz sería tan útil como una tetera de chocolate. Todo lo que podría hacer es tomar copias de toda la matriz o reemplazar toda la matriz a partir de una copia.

    -Richard Hodges

    6 de septiembre de 2017 a las 8:06

  • Breve y elegante explicación. Gracias. Esta es la respuesta que busqué.

    – arcoíris

    6 sep 2017 a las 8:20

  • cppref estados “std::atomic puede ser instanciado con cualquier TriviallyCopyable tipo T:” y las matrices afaik son trivialmente copiables, ¿o me equivoco?

    – 463035818_no_es_un_número

    6 de septiembre de 2017 a las 8:26

  • @RichardHodges: Sí, escribí esto antes de ir en bicicleta al trabajo. Cuando salí por la puerta, ¡me di cuenta de ese problema!

    – Martin Bonner apoya a Mónica

    6 de septiembre de 2017 a las 8:33

  • @tobi303: Hmmm. De acuerdo con cppreference, las matrices de tipos trivialmente copiables son trivialmente copiables. Eso puede significa que hay un error en cppreference, o hay un defecto en el estándar. Requerir que la biblioteca le permita crear instancias std::atomic<int[10]> cuando no puede haber forma de obtener el valor (porque operator T tendría que volver int[10]) no tiene sentido.

    – Martin Bonner apoya a Mónica

    6 de septiembre de 2017 a las 8:46

Puede poner matrices en atómicas, pero no directamente. Al igual que la otra respuesta, explica que puedes usar std::array. Respondí esta pregunta y expliqué cómo hacer algo similar para una estructura.

Dicho esto y explicada la viabilidad técnica, tengo que deciros algo más:

POR FAVOR NO HAGAS ESO

El poder de las variables atómicas proviene del hecho de que algunos procesadores pueden realizar sus operaciones con una sola instrucción. El compilador de C++ intentará que sus operaciones atómicas sucedan en una sola instrucción. Si falla, iniciará un bloqueo de autobús, que es como un bloqueo global de todo, hasta que se actualice esa matriz. Es equivalente a un mutex que bloquea todas sus variables en su programa. Si le preocupa el rendimiento, ¡no lo haga!

Entonces, para su caso, un mutex no es una mala idea. Al menos puede controlar lo que es crítico y mejorar el rendimiento.

  • Bloqueo de autobús: ¿Tiene una cita para eso? Seguramente, crear un mutex bajo el capó será mucho más fácil de implementar (y mucho más eficaz).

    – Martin Bonner apoya a Mónica

    6 de septiembre de 2017 a las 8:32

  • ¿Cómo puede hacerse esto? Fácil. Ninguna de las funciones miembro de std::atomic permitir el acceso directo al valor subyacente, así que dé a cada std::atomic un mutex, bloquee el mutex al comienzo de cada función miembro, suéltelo al final. tengo un sentimiento que puede estar recordando mal cómo algunas arquitecturas implementan operaciones atómicas, por ejemplo, en una palabra.

    – Martin Bonner apoya a Mónica

    6 de septiembre de 2017 a las 8:43

  • @MartinBonner Puede que tengas razón. Pero permítanme señalar un punto muy sutil aquí: no se trata de la seguridad de subprocesos, ya que esto no es lo que el programa tiene que garantizar, y ese es el problema. Se trata de atomicidad y su significado semántico. La definición de una variable atómica es: Es una variable con operaciones que se pueden hacer o no hacer. No puede estar en ningún estado intermedio. No se trata de bloquear el acceso a las variables. Pero nuevamente, podría estar recordando mal esto, sin embargo, nunca haría atómicos por más de 16 bytes.

    – El físico cuántico

    6 de septiembre de 2017 a las 8:51


  • std::atomic<std::array<unsigned char, 256>> proporciona exactamente eso. Internamente, puede haber estados intermedios, pero no son observables por ningún programa C++ con un comportamiento definido. (Y si el programa tiene un comportamiento indefinido, se vuelve imposible razonar).

    – Martin Bonner apoya a Mónica

    6 sep 2017 a las 8:55

  • Siento que “si falla, iniciará un bloqueo de autobús” no es exactamente cierto. Fíjate en la firma de atomic::load: cplusplus.com/reference/atomic/atomic/carga Puedes especificar memory_order_xxx

    – KeksArmée

    12 de septiembre de 2018 a las 16:28

¿Ha sido útil esta solución?