2012-10-03 34 views
8

La norma dice: "Un objeto de tipo thread :: id proporciona ... un único valor distinto para todos los objetos de subprocesos que no representan un subproceso de ejecución". ¿Es ese un valor único/distinto con respecto a operator==, o es el valor real a nivel de bit único/distinto?Requisitos para std :: thread :: id. ¿Puede ser atomizado?

El motivo de la pregunta: std::thread::id::id() de MSVC2012 deja basura en uno de sus campos, y rompe el código que compara-exchange en un std::atomic<std::thread::id> (ya que este último depende de las comparaciones bit a bit).

¿Es std::atomic<std::thread::id> una construcción legal en primer lugar?

EDIT: para la referencia, el código es el siguiente:

while(!worker_id.compare_exchange_weak(no_id = thread_id_type(), self_id)) 
    sleep(); 

Respuesta

10

En primer lugar, std::atomic<std::thread::id> es legal: std::thread::id debe ser copiable trivialmente (30.3.1.1p2), que cumpla con los requisitos de std::atomic<> (29.5p1).

Sin embargo, es una clase opaca, por lo que no es necesario que el patrón de bits de los objetos que comparan iguales sea idéntico.

En consecuencia, si usa compare_exchange_weak o compare_exchange_strong, es posible que falle para valores que se igualen.

Así, el consejo es utilizar compare_exchange_weak en un bucle, dejando el valor expected como resultado de la iteración anterior.

En su caso, la semántica que interpretar a partir de su bucle son: seguirá sonando mientras worker_id es el ID de otro hilo, o worker_id era std::thread::id pero el intercambio no. Esto se puede conseguir con lo siguiente:

no_id=std::thread::id(); 
while((no_id!=std::thread::id()) || 
     !worker_id.compare_exchange_weak(no_id, self_id)){ 
    if(no_id!=std::thread::id()) no_id=std::thread::id(); 
    sleep(); 
} 

o

no_id=std::thread::id(); 
while(!worker_id.compare_exchange_weak(
      (no_id!=std::thread::id())?(no_id=std::thread::id())?no_id, self_id)) 
    sleep(); 

es decir, solamente cambiar el valor no_id si es nostd::thread::id().

+0

Gracias.Reestablecer selectivamente 'no_id' es un buen truco, ahora estoy empezando a preguntarme por qué no lo vi :) – vpozdyayev

+0

Pero probablemente solo quieras llamar a sleep si" no_id! = Std :: thread :: id() " en el lazo. – cmeerw

+0

@cmeerw Sí --- Estaba intentando replicar el bucle de vpozdyayev lo más cerca posible. Si 'compare_exchange_weak' falla" de forma espuria ", entonces en la mayoría de los casos, quiere hacer un bucle inmediatamente, sin esperar. –

5

Esto fue discutido en LWG924. Básicamente, no puede usar compare_exchange_strong, pero debería poder usar compare_exchange_weak en un bucle, p.

expected = current.load(); 
do { 
    desired = function(expected); 
} while (!current.compare_exchange_weak(expected, desired)); 

Editar: Incondicionalmente restablecer el valor en contra del propósito del bucle - basado en el código suministrado, creo que sería entonces la mejor solución:

no_id = std::thread::id(); 
while(!worker_id.compare_exchange_weak(no_id, self_id)) 
{ 
    if (no_id != std::thread::id()) 
    { 
    sleep(); 
    no_id = std::thread::id(); 
    } 
} 
+0

Estoy usando 'compare_exchange_weak' en un bucle y, por desgracia, no ayuda. Por cierto, el problema aquí son los campos no inicializados, no el relleno (mencionado como un "problema relacionado pero separable" en su enlace). Soy consciente de la nota 29.6.5/26 sobre la semántica de memcmp y compare_exchange_weak convergiendo rápidamente, pero todavía no veo cómo podría funcionar si 'current' y' expected' son representaciones bit a bit diferentes de la misma (como por 'operador ==') valor. – vpozdyayev

+0

Agregó el ejemplo de código a la pregunta. – vpozdyayev

Cuestiones relacionadas