2012-06-19 7 views
5

Duplicar posibles:
C# Captured Variable In Loopque rosca simple en C#

estoy trabajando en algunas aplicaciones simples de rosca, pero me parece que no puede conseguir que esto funcione:

class ThreadTest 
{ 
    static Queue<Thread> threadQueue = new Queue<Thread>(); 

    static void Main() 
    { 
     //Create and enqueue threads 
     for (int x = 0; x < 2; x++) 
     { 
      threadQueue.Enqueue(new Thread(() => WriteNumber(x))); 
     } 

     while(threadQueue.Count != 0) 
     { 
      Thread temp = threadQueue.Dequeue(); 
      temp.Start(); 
     } 

     Console.Read(); 
    } 

    static void WriteNumber(int number) 
    { 
     for (int i = 0; i < 1000; i++) 
     { 
      Console.Write(number); 
     } 
    } 
} 

El objetivo básicamente es agregar hilos a una cola uno por uno, y luego pasar por la cola uno por uno y saltar un hilo y ejecutarlo. Como tengo "x < 2" en mi bucle for, solo debe hacer dos hilos, uno donde ejecuta WriteNumber (0) y otro donde ejecuta WriteNumber (1), lo que significa que debería terminar con 1000 0's y 1000 1 en mi pantalla en orden variable dependiendo de cómo se ejecutan finalmente los hilos.

Lo que termino con 2000 es 2. Las dos soluciones posibles que he encontrado son: me he perdido algo obvio que es obvio, o enviar la variable x a la función WriteNumber es hacer una referencia de paso a paso y no pasar por valor, de modo que cuando el Los subprocesos ejecutan que están utilizando la versión más reciente de x en lugar de lo que era en el momento en que se estableció la función. Sin embargo, tenía entendido que las variables se pasan por valor de manera predeterminada en C#, y solo se pasan por referencia si incluye 'ref' en su parámetro.

+2

Una cola de hilos es ciertamente una idea inusual. – CodesInChaos

+0

no es una idea improbable que es lo que threadpool hace internamente. así que use threadpool instad. – DarthVader

+0

Threadpools generalmente tienen una cola de instancias de clases de tareas y, si se les puede molestar realizar un seguimiento, una lista fija de hilos que dequeue y ejecuta las tareas. Micromanaging hilos en listas/colas generalmente conduce a un desastre. He visto algunos intentos de esto en SO - grandes cantidades de código tratando de sondear si los hilos en las listas han "terminado", liberarlos y citar nuevos para completar el agujero en el conjunto. Verdaderamente horrible ... –

Respuesta

18

Está capturando x en la expresión lambda. El valor de x cambia a 2 antes de iniciar los subprocesos. Es necesario hacer una copia del valor dentro del bucle:

for (int x = 0; x < 2; x++) 
{ 
    int copy = x; 
    threadQueue.Enqueue(new Thread(() => WriteNumber(copy))); 
} 

expresiones lambda capturan las variables . A pesar de que el valor pasado a WriteNumberes pasa por valor, no se le llama en absoluto hasta que se inicie el hilo - momento en el cual x es 2.

Al hacer una copia dentro del bucle, cada iteración del bucle se su propia "instancia" separada de la variable copy, y que no cambia el valor ... de modo que cuando se llama WriteNumber, cada variable copy tiene el mismo valor que x tenía para esa iteración.

+0

¡Fascinante, gracias! ¡Parece que tengo que investigar mucho más sobre las expresiones lambda! – Jake

9

Esto ocurre porque el valor de x se accede después de se termina el bucle, y en ese momento es 2.

Debe usar una variable temporal para evitar la captura de variables.

for (int x = 0; x < 2; x++) 
{ 
    int tmp = x; 
    threadQueue.Enqueue(new Thread(() => WriteNumber(tmp))); 
}