¿Cómo asignar el almacenamiento local de subprocesos?

Tengo una variable en mi función que es estática, pero me gustaría que fuera estática por hilo.

¿Cómo puedo asignar la memoria para mi clase C ++ de modo que cada hilo tenga su propia copia de la instancia de la clase?

AnotherClass::threadSpecificAction()
{
  // How to allocate this with thread local storage?
  static MyClass *instance = new MyClass();

  instance->doSomething();
}

Esto está en Linux. No estoy usando C ++ 0x y esto es gcc v3.4.6.

preguntado el 16 de mayo de 11 a las 17:05

Depende de si está en Windows o en otro lugar. -

depende. si usa boost: drdobbs.com/cpp/184401518?pgno=6 -

Debe proporcionar más información sobre la plataforma real, incluido si puede / desea usar las funciones de C ++ 0x (que pueden estar o no disponibles en su plataforma, por lo que nuevamente, la plataforma es importante: sistema operativo, compilador y versión). ) -

9 Respuestas

#include <boost/thread/tss.hpp>
static boost::thread_specific_ptr< MyClass> instance;
if( ! instance.get() ) {
    // first time called by this thread
    // construct test element to be used in all subsequent calls from this thread
    instance.reset( new MyClass);
}
    instance->doSomething();

contestado el 09 de mayo de 13 a las 17:05

Esto. No se moleste con cosas específicas del sistema operativo, boost::thread_specific_ptr es limpio, portátil e idiomático. - Alexander C.

@entheh: ¿Por super-vago te refieres a aquellos que valoran el código de ejemplo bien construido? =). - Claudiu

Solo tenga en cuenta que la limpieza de * instancia solo se realiza si el hilo de salida es un boost :: hilo - jupp0r

La pregunta original es acerca de los subprocesos locales "estáticos", que es exactamente lo que está integrado en C ++ a través del thread_local palabra clave, por lo que debería usar el soporte nativo de C ++ tan simple como thread_local MyClass instance;. El thread_specific_ptr El código en esta respuesta es más complicado y menos conforme con los estándares. Aunque no es la elección correcta aquí, thread_specific_ptr sigue siendo útil para dinamicamente asignación de objetos locales de subproceso. - user2913094

@ user2913094 Como era de esperar, GCC 3.4 no tiene esa característica. Además, GCC 4.6 ni MSVC thread_local para tipos no triviales, lo que significa que ni siquiera puede tener un unique_ptr o shared_ptr allí. Esto significa que en los compiladores actuales thread_local puede ser imposible de usar sin perder memoria. - sehe

Vale la pena señalar que C ++ 11 presenta la thread_local palabra clave.

Aquí hay un ejemplo de Especificadores de duración de almacenamiento:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>

thread_local unsigned int rage = 1; 
std::mutex cout_mutex;

void increase_rage(const std::string& thread_name)
{
    ++rage;
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}

int main()
{
    std::thread a(increase_rage, "a"), b(increase_rage, "b");
    increase_rage("main");

    a.join();
    b.join();

    return 0;
}

Salida posible:

Rage counter for a: 2
Rage counter for main: 2
Rage counter for b: 2

Respondido 04 Abr '15, 21:04

GCC agregó soporte para thread_local en 4.8. Si usa 4.7 o una versión anterior, deberá usar __thread en lugar de especificar la clase de almacenamiento. - Dibujó Noakes

Eso es exactamente lo que necesito. Pero por que necesito mutex? La ciencia thead_local es privado para cada hilo? - usuario2269707

@reavenisadesk, no debería necesitar un mutex para cualquier thread_local variable. - russoue

Desafortunadamente, hacer que thread_local funcione en OSX es una pesadilla: stackoverflow.com/questions/28094794/… - Alex Jansen

@reavenisadesk, El mutex simplemente sincroniza la salida, porque la secuencia no escribe todo a la vez. Sin el candado, leería algo como "Contador de ira para contador de ira para un contador de ira para: principal" ... - Youka

boost::thread_specific_ptr es la mejor manera como solución portátil.

En Linux y GCC puede usar __thread modificador.

Entonces su variable de instancia se verá así:

static __thread MyClass *instance = new MyClass();

contestado el 24 de mayo de 13 a las 21:05

Tenga cuidado con el destructor que no se ejecuta en MyClass cuando se usa __thread. (impulso :: thread_specific_ptr o thread_local MyClass ejecutan el destructor.) - David L.

Si está utilizando Pthreads, puede hacer lo siguiente:

//declare static data members
pthread_key_t AnotherClass::key_value;
pthread_once_t AnotherClass::key_init_once = PTHREAD_ONCE_INIT;

//declare static function
void AnotherClass::init_key()
{
    //while you can pass a NULL as the second argument, you 
    //should pass some valid destrutor function that can properly
    //delete a pointer for your MyClass
    pthread_key_create(&key_value, NULL);
}

void AnotherClass::threadSpecificAction()
{
  //Initialize the key value
  pthread_once(&key_init_once, init_key);

  //this is where the thread-specific pointer is obtained
  //if storage has already been allocated, it won't return NULL

  MyClass *instance = NULL;
  if ((instance = (MyClass*)pthread_getspecific(key_value)) == NULL)
  {
    instance = new MyClass;
    pthread_setspecific(key_value, (void*)instance);
  }

  instance->doSomething();
}

contestado el 16 de mayo de 11 a las 22:05

Si está trabajando con MSVC ++, puede leer Almacenamiento local de subprocesos (TLS)

Y luego puedes ver esto ejemplo.

Además, tenga en cuenta la Reglas y limitaciones de TLS

contestado el 16 de mayo de 11 a las 21:05

Votado en contra porque a la luz de las reglas y limitaciones, no veo cómo eso responde a la pregunta. No hay mención de C2482 or C2483. - Andreas Haferburg

Voto a favor porque el título de esta pregunta no menciona Linux. Y el tráfico de búsqueda se basa en el título, no en algunas renuncias sutiles en el cuerpo de la pregunta. Sin embargo, esos son enlaces útiles. - aplazamiento de pago

C ++ 11 especifica un thread_local tipo de almacenamiento, solo utilícelo.

AnotherClass::threadSpecificAction()
{
  thread_local MyClass *instance = new MyClass();
  instance->doSomething();
}

Una optimización opcional es asignar también en el almacenamiento local de subprocesos.

Respondido 17 ago 16, 13:08

Sin embargo, no implementado en OS X o iOS. - jupp0r

@ jupp0r, entonces su compilador de OS X no es compatible con C ++ 11. dcl.stc/1 : especificador de clase de almacenamiento: estático, thread_local, extern, mutable. - rustyx

@rustyx: sí, AppleClang no es compatible con C ++ 11 (a partir de ahora, es compatible con thread_local viene en XCode8). - jupp0r

En Windows puedes usar TlsAlloc y TlsFree para asignar almacenamiento en el almacenamiento local de subprocesos.

Para establecer y recuperar valores con TLS, puede usar TlsSetValue y TlsGetValue, Respectivamente

Aquí puedes ver un ejemplo de cómo se usaría.

contestado el 16 de mayo de 11 a las 22:05

Solo una nota al margen ... MSVC ++ admite declspec (hilo) de VSC ++ 2005

#if (_MSC_VER >= 1400)
  #ifndef thread_local     
    #define thread_local __declspec(thread)
  #endif
#endif

El problema principal es (que se resuelve en boost :: thread_specific_ptr) las variables marcadas con no pueden contener ctor o dtor.

respondido 01 nov., 14:06

El problema se resuelve en C ++ 11, que es compatible con MSVC 2015 (y tal vez 2013). - rustyx

Folly (biblioteca de código abierto de Facebook) tiene una implementación portátil de Thread Local Storage.

Según sus autores:

Almacenamiento local de subprocesos mejorado para tipos no triviales (velocidad similar a pthread_getspecific pero solo consume una pthread_key_ty 4 veces más rápido que boost::thread_specific_ptr).

Si está buscando una implementación portátil de Local Storage Thread, esta biblioteca es una buena opción.

Respondido el 22 de junio de 16 a las 18:06

Sin embargo, no es compatible con Windows (tanto para portátiles). - jupp0r

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.