2012-09-17 15 views
7

Tengo un lambda dentro de un bucle for con el parámetro variable de bucle en el lambda. Cuando lo ejecuto, espero que salgan los números 0-9. Pero como es una lambda, la x no se evalúa inmediatamente.Lambda dentro del bucle

for(int x = 0; x < n; ++x) 
    { 
      vec.push_back(thread{[&x](){ 
        m.lock(); 
        cout << x << endl; 
        m.unlock(); 
      }}); 
    } 

Salida:

0 
3 
3 
9 

etc.

La solución para otros idiomas sería la creación de una variable temporal,

for(int x = 0; x < n; ++x) 
    { 
      int tmp = x; 
      vec.push_back(thread{[&tmp](){ 
        m.lock(); 
        cout << tmp << endl; 
        m.unlock(); 
      }}); 
    } 

pero eso no parece funcionar .

ver Threads receiving wrong parameters

Bono:

En mi búsqueda de una respuesta, me encontré con este problema Generalizing C++11 Threads class to work with lambda que no recomienda el uso de un recipiente que pueda invalidar los iteradores. ¿Por qué sería/

Respuesta

11

Cuando especifica la captura, puede elegir entre la captura por valor y la captura por referencia. Has elegido capturar por referencia. La captura por referencia significa que la variable dentro de la función lambda se refiere al mismo objeto. La implicación es que cualquier cambio en esta variable se compartirá y también deberá asegurarse de que el objeto al que se hace referencia permanezca durante toda la vida de la función lambda.

Probablemente quisiste capturar por valores. Para hacer esto, puede reemplazar la especificación de captura para convertirse en [=] o convertirse en [x]. Este último se asegura de que solo se pueda acceder a x, mientras que el primero permitiría el acceso a otras variables.

Por cierto, me gustaría recomendar no usando lock() y unlock() explícitamente, sino más bien utilizar uno de los guardias de seguridad. Con esto, el cuerpo de su bucle sería algo como esto:

vec.push_back(std::thread{[x](){ 
    std::lock_guard<std::mutex> kerberos(m); 
    std::cout << x << "\n"; 
}}); 
+0

Me siento un poco tonto. No entendí para qué era el & en las expresiones lambda. Para este ejemplo simplificado, sí, un lock_guard sería apropiado. Pero en la aplicación real, la función hace mucho que no necesita ser bloqueada. En ese caso, un mutex sería apropiado para bloquear y desbloquear solo alrededor de la parte importante, ¿correcto? – SaulBack

+5

@SaulBack: No. Deberías usar 'lock_guard' de cualquier forma. Si solo quiere bloquear cierto código, use un bloque explícito con '{}' para encerrar el código bloqueado. Esta es la codificación RAII básica aquí. –

+1

@SaulBack: especialmente en el código complicado que desea utilizar RAII, por ejemplo, 'std :: lock_guard '! En este ** ejemplo ** trivial puedo ver que no es necesario, pero en el momento en que es un poco más complejo las cosas se ponen rápidamente fuera de control. Si necesita restringir el alcance donde se guarda el bloqueo, use un alcance, es decir, un par de llaves: '{}'. –

6

captura el parámetro por valor en lugar de por referencia si desea hacer una copia:

vec.push_back(std::thread{[x](){ 
    m.lock(); 
    std::cout << x << std::endl; 
    m.unlock(); 
}}); 

Esto copiará el valor de x en el momento del objeto lambda fue creado (no en el momento en que se inició el hilo).

En mi búsqueda de una respuesta, me encontré con esta pregunta Generalizando C++ 11 Subprocesos clase para trabajar con lambda, que recomienda no utilizar un contenedor que invalidaría los iteradores. ¿Por qué sería eso/

Porque se trata de una implementación completamente diferente que utiliza directamente la biblioteca pthreads. Está utilizando std::thread, que está diseñado para funcionar en C++.

+0

¿Puede explicar por qué ptheads en un recipiente no funcionarían mientras que un std :: hilo haría? ¿Es una cuestión de copiar constructores, mover constructores, operadores de asignación? – SaulBack

3

El problema es que está capturando la x por referencia. Por lo tanto, cuando x se incrementa al final de cada ciclo, eso se refleja en el hilo.

Desea capturar x por valor para que lambda solo use el valor de x cuando se crea lambda.

for(int x = 0; x < n; ++x) 
{    
    vec.push_back(thread{[x](){ 
     m.lock(); 
     cout << tmp << endl; 
     m.unlock(); 
    }}); 
} 
Cuestiones relacionadas