Cómo obtener la identificación del hilo entero en c ++ 11

84

c ++ 11 tiene la posibilidad de obtener la identificación del hilo actual, pero no se puede convertir en un tipo entero:

cout<<std::this_thread::get_id()<<endl;

salida: 139918771783456

cout<<(uint64_t)std::this_thread::get_id()<<endl;

error: conversión no válida del tipo 'std :: thread :: id' para escribir 'uint64_t' lo mismo para otros tipos: conversión no válida del tipo 'std :: thread :: id' para escribir 'uint32_t'

Realmente no quiero hacer una conversión de punteros para obtener la identificación del hilo entero. ¿Existe alguna forma razonable (estándar porque quiero que sea portátil) de hacerlo?

NoSenseEtAl
fuente
13
¿Para qué necesitas que sea un número entero? Se garantiza que no tiene sentido hacer aritmética de ningún tipo en él, y no es significativo fuera del contexto del proceso, por lo que no debería ser necesario serializarlo más que para la depuración (que operator<<parece manejar bien).
hmakholm dejó a Monica el
4
algo como esto: 1024cores.net/home/lock-free-algorithms/false-sharing---false pero en lugar de N = MAX_THREAD_COUNT tendré algo como N = 128 y haré thread_id% N
NoSenseEtAl
9
Si realmente desea que sea portátil, debe estar preparado para la posibilidad de que thread::idno se represente como un número entero. La página a la que enlaza utiliza una matriz, indexada por ID de hilo. ¿Ha considerado usar un map<thread::id, int>en su lugar? Luego, puede utilizar los operadores relacionales ya definidos para la idclase sin realizar ninguna conversión. El estándar también define hash<thread::id>, por lo que también puede usar los contenedores desordenados.
Rob Kennedy
3
@Rob ese mapa requeriría mutex :(
NoSenseEtAl
1
@SwissFrank o debería decir CHF: PI todavía estoy presente, pero creo que la respuesta aceptada está bien para mí, depende de mí asegurarme de que los valores de identificación de variable sean únicos durante la duración de un programa.
NoSenseEtAl

Respuestas:

33

La solución portátil es pasar sus propios ID generados al hilo.

int id = 0;
for(auto& work_item : all_work) {
    std::async(std::launch::async, [id,&work_item]{ work_item(id); });
    ++id;
}

El std::thread::idtipo debe usarse solo para comparaciones, no para aritmética (es decir, como dice en la lata: un identificador ). Incluso su representación de texto producida por nooperator<< está especificada , por lo que no puede confiar en que sea la representación de un número.

También puede usar un mapa de std::thread::idvalores para su propia identificación y compartir este mapa (con la sincronización adecuada) entre los subprocesos, en lugar de pasar la identificación directamente.

R. Martinho Fernandes
fuente
1
¡Ajá! Pero no es una representación de texto! Eso es lo suficientemente bueno para que los humanos encuentren visualmente una distinción entre ellos, ¿verdad?
Xunie
La solución thread :: id (o this_thread :: get_id ()) mencionada aquí es la mejor, porque no es específica del programador. Vea la respuesta de secuencia de cadenas de Mike a continuación para obtener una representación de cadena o entero.
Andrew
@Andrew Me referí a eso en la respuesta: "Incluso su representación de texto producida por el operador << no está especificada, por lo que no puede confiar en que sea la representación de un número". Parece que se dispone de una definición turbia de la palabra "mejor".
R. Martinho Fernandes
"mejor" no estaba en relación con la representación de la cadena.
Andrew
1
Además, acabo de hacer un punto de referencia con 10,000,000 iteraciones por mi propio bien y this_thread :: get_id () es increíblemente rápido: pastebin.com/eLa3rKQE El modo de depuración toma 0.0000002543827 segundos por llamada y Release toma 0.00000003652367 segundos por llamada para mí. (Intel i5 2.60 GHz)
Andrew
85

Solo necesitas hacer

std::hash<std::thread::id>{}(std::this_thread::get_id())

para conseguir un size_t.

Desde cppreference :

La especialización de la plantilla std::hashpara la std::thread::idclase permite a los usuarios obtener hashes de los identificadores de subprocesos.

888
fuente
35
Creo que esto tiene que ser así std::hash<std::thread::id>()(std::this_thread::get_id()), ¿no?
Barry
12
¿Se garantizaría que el hash sea único? Probablemente no, anulando su uso como identificador de hilo único.
Michael Goldshteyn
2
El ejemplo dado no funciona con al menos Clang 3.4 y libstdc ++ 4.8. Sin embargo, la reformulación de Barry funciona.
Arto Bendiken
3
gracias 888 por la respuesta. El compilador de MS tiene thread :: id :: hash () pero el código de Barry cumple con los estándares. Los hashes pueden chocar. Todavía es útil tener un hash por hilo (con suerte, una probabilidad de colisión cercana a 0)
a.lasram
1
MSVC en realidad devuelve un ID de hilo con hash en este caso. También podría generar su propio ...
rustyx
25

Otra identificación (¿idea? ^^) sería usar cadenas de cadenas:

std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());

Y use try catch si no desea una excepción en caso de que las cosas salgan mal ...

Miguel
fuente
2
Buena respuesta. Esto serviría al propósito en general.
iammilind
5
Esto no es portátil, ya que no hay garantía de que se std::thread::idimprima como caracteres que componen un número entero de la misma manera que no se garantiza que la identificación del hilo esté representada internamente por un número entero.
blubberdiblub
1
@Nikos siempre que una implementación elija que un número entero es insuficiente. O cuando lo considere inapropiado por cualquier otro motivo. El punto aquí es que cuando la especificación no lo especifica como un número entero (y no lo hace, solo tiene algunas garantías más abstractas), no puede ni debe confiar en que sea un número entero en cualquier implementación. Simplemente utilícelo std::thread::idcomo tipo en lugar de un número entero, para eso existe. Y no reinterprete su representación de cadena como dígitos que forman un número. Trátelo como opaco o como salida de depuración / registro.
blubberdiblub
6

Una idea sería usar el almacenamiento local de subprocesos para almacenar una variable, sin importar el tipo, siempre que cumpla con las reglas del almacenamiento local de subprocesos, luego usar la dirección de esa variable como su "ID de subproceso". Obviamente, cualquier arithemética no tendrá sentido, pero será un tipo integral.

Para la posteridad: pthread_self()devuelve pid_tay es posix. Esto es portátil para alguna definición de portátil.

gettid(), es casi seguro que no es portátil, pero devuelve un valor compatible con GDB.

tgoodhart
fuente
pthread_self()en realidad devuelve un pthread_t, que es opaco (a diferencia de pid_t(devuelto por gettid()) que, aunque también es específico de la plataforma, aparentemente es un número entero, al menos). Pero +1 para el primer bit, ¡resolvió mi problema!
Cameron
4

Realmente no sé qué tan rápido es esto, pero esta es la solución que logré crear:

const size_t N_MUTEXES=128;//UINT_MAX,not 128  for answer to my original question
hash<std::thread::id> h;
cout<<h(std::this_thread::get_id())%N_MUTEXES<<endl;

Nuevamente, estoy empezando a pensar que obtener un puntero a la estructura y convertirlo en unsigned int o uint64_t es la respuesta ... EDITAR:

uint64_t get_thread_id()
{
    static_assert(sizeof(std::thread::id)==sizeof(uint64_t),"this function only works if size of thead::id is equal to the size of uint_64");
    auto id=std::this_thread::get_id();
    uint64_t* ptr=(uint64_t*) &id;
    return (*ptr);
}
int main()
{
    cout<<std::this_thread::get_id()<<"  "<<get_thread_id()<<endl;
}

static_assert para evitar problemas infernales :) Reescribir es fácil en comparación con la búsqueda de este tipo de error. :)

NoSenseEtAl
fuente
3
No tiene garantías de que no obtendrá valores duplicados con la hashfunción, mucho menos si la% .
R. Martinho Fernandes
1
¡No puedes conseguir esa garantía con std::this_thread::get_id()! Pero probablemente no lo necesite. Un par de hilos compartidos entre sí no crea el mismo problema masivo que cada hilo compartido con todos los demás hilos. Algo como const size_t N_COUNTERS = 128; struct Counter { std::atomic<int> counter; char pad[CACHE_LINE_SIZE - sizeof(atomic<int>); } counters[N_COUNTERS];probablemente esté bien. (Un bloqueo atómico o de giro para una sincronización muy ligera.)
Scott Lamb
@R. Martinho Fernandes Como dije, estoy interesado en el valor int, así que puedo%, las colisiones están bien si son raras, básicamente lo que dijo Scott.
NoSenseEtAl
1
De hecho, probé esto y estaba completamente equivocado: usar en atomic<int>lugar de intes una desaceleración dramática incluso sin contención.
Scott Lamb
1
Puede reemplazar static_assert con algo como este ideone.com/Q7Nh4 (fácilmente modificable para hacer cumplir un requisito de tamaño exacto si lo desea) para que funcione de manera más portátil (tenga en cuenta que ideone tiene una identificación de subproceso de 32 bits, por ejemplo) .
R. Martinho Fernandes
4

thread::native_handle()devuelve thread::native_handle_type, que es un typedef de long unsigned int.

Si el subproceso se construye por defecto, native_handle () devuelve 0. Si hay un subproceso del sistema operativo adjunto, el valor de retorno es distinto de cero (es pthread_t en POSIX).

Alexey Polonsky
fuente
¿Dónde se especifica que std::thread::native_handle_typees un typedef long unsigned? En 30.3.1 / 1 solo podemos vertypedef implementation-defined native_handle_type; // See 30.2.3
Ruslan
Una forma tonta pero sencilla de descubrir el tipo es generar un error de compilación deliberado asignando thread :: native_handle () a, por ejemplo, uint8_t. Luego, el compilador se quejará de la falta de coincidencia de tipos y también le dirá cuál es el tipo.
Alexey Polonsky
1
Bueno, eso no es portátil ya que depende de una implementación particular.
Ruslan
Bueno, al menos si la implementación subyacente usa POSIX pthread, parece que native_handle () debe ser un pthread_t. Ahora, pthread_t es un tipo de puntero (typedef struct pthread * pthread_t). Por lo tanto, tiene sentido que std :: thread :: native_handle_type sea un tipo entero capaz de contener un puntero (por ejemplo, size_t o unsigned long).
Alexey Polonsky
3

De esta forma, debería funcionar:

std::stringstream ss;
ss << std::this_thread::get_id();
int id = std::stoi(ss.str());

Recuerde incluir el flujo de la biblioteca

Federico Rizzo
fuente
Bien, pero ¿por qué asume que es un número entero? Puede ser hexadecimal o cualquier otra cosa.
rustyx
si lo está usando std::stringstream, puede usarlo operator >>para convertirlo a int. De hecho, preferiría uint64_tcomo tipo de en idlugar de intsi estoy seguro de que ides integral.
aniliitb10
3

Una razón clave para no usar thread :: get_id () es que no es único en un solo programa / proceso. Esto se debe a que la identificación se puede reutilizar para un segundo hilo, una vez que finaliza el primer hilo.

Esto parece una característica horrible, pero es lo que está en c ++ 11.

midjji
fuente
2

depende de para qué quieres usar el thread_id; puedes usar:

std::stringstream ss;
ss << std::this_thread::get_id();
uint64_t id = std::stoull(ss.str());

Esto generará una identificación única dentro de su proceso; pero hay una limitación: si lanza varias instancias del mismo proceso y cada una de ellas escribe sus ID de subproceso en un archivo común, la unicidad del thread_id no está garantizada; de hecho, es muy probable que tenga superposiciones. En este caso, puede hacer algo como:

#include <sys/time.h>
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
uint64_t id = (ts.tv_sec % 1000000000) * 1000000000 + ts.tv_nsec;

ahora se le garantizan identificadores de hilo únicos en todo el sistema.

Pandrei
fuente
El sobrecargado operator<<puede imprimir cualquier cosa , es incorrecto asumir que siempre imprimirá un número entero.
rustyx
2

Otra alternativa:

#include <atomic>

static std::atomic<unsigned long long> thread_counter;

unsigned long long thread_id() {
    thread_local unsigned long long tid = ++thread_counter;
    return tid;
}

El código generado para esta función por g ++ en x86 de 64 bits es simplemente:

_Z9thread_idv:
        cmp     BYTE PTR fs:_ZGVZ9thread_idvE3tid@tpoff, 0
        je      .L2
        mov     rax, QWORD PTR fs:_ZZ9thread_idvE3tid@tpoff
        ret
.L2:
        mov     eax, 1
        lock xadd       QWORD PTR _ZL14thread_counter[rip], rax
        mov     BYTE PTR fs:_ZGVZ9thread_idvE3tid@tpoff, 1
        mov     QWORD PTR fs:_ZZ9thread_idvE3tid@tpoff, rax
        ret
_ZGVZ9thread_idvE3tid:
        .zero   8
_ZZ9thread_idvE3tid:
        .zero   8

Es decir, una sola rama sin ninguna sincronización que se predecirá correctamente excepto la primera vez que llame a la función. Después de eso, solo un único acceso a la memoria sin sincronización.

6502
fuente
@NoSenseEtAl: No estoy seguro de entender su pregunta ... thread_localya describe la duración del almacenamiento tid. El staticde thread_counteres porque no quiere exponer que, fuera de esta unidad de compilación.
6502
Este tipo de extrañamente asigna ID de hilo en el orden en que consulta ID de hilo. (Yo mismo hice algo MUY similar, y nunca me gustó esta rareza). También asigna desde cero, lo que no es habitual. (Por ejemplo, GDB informa ID de subprocesos a partir de 1.)
Swiss Frank
1
@SwissFrank: es solo un número y no debería leer demasiado en el valor devuelto: no hay forma legal de saber que fue asignado cuando lo consultó :-). Sobre el hecho de que 0es una identificación válida, es un buen punto y se puede arreglar usando preincrement en su lugar. Cambiaré la respuesta para hacer eso.
6502
1

Quizás esta solución sea útil para alguien. Llámalo im primera vez main(). Advertencia: namescrece indefinidamente.

std::string currentThreadName(){
    static std::unordered_map<std::thread::id,std::string> names;
    static std::mutex mtx;

    std::unique_lock<std::mutex> lock(mtx);

    auto id = std::this_thread::get_id();

    if(names.empty()){
        names[id] = "Thread-main";
    } else if(names.find(id) == names.end()){
        std::stringstream stream;
        stream << "Thread-" << names.size();
        names[id] = stream.str();
    }

    return names[id];
}
geh
fuente
no use stringstream, es lento, use std :: to_string
NoSenseEtAl