2012-04-22 12 views
7

Según el MSDN Documentation TaskFactory.StartNew Crea e inicia una Tarea. Así, para el ejemplo de código¿Por qué TaskFactory.StartNew Task no está comenzando inmediatamente?

class Program 
{ 
    public static void Main() 
    { 
     var t =Task.Factory.StartNew(
       () => SomeLongRunningCalculation(10, Display) 
      ); 
     var t1 = Task.Factory.StartNew(
       () => SomeLongRunningCalculation(20, Display) 
      ); 
     Console.WriteLine("Invoked tasks"); 
     Task.WaitAll(t, t1); 
     Console.ReadLine(); 
    } 

    public static void Display(int value) 
    { 
     Console.WriteLine(value); 
    } 

    public static void SomeLongRunningCalculation(int j, Action<int> callBack) 
    { 
     Console.WriteLine("Invoking calculation for {0}", j); 
     System.Threading.Thread.Sleep(1000); 
     if (callBack != null) 
     { 
      callBack(j + 1); 
     } 
    } 
}  

Mi salida esperada era

Invoking calculation for 10 
Invoking calculation for 20 
Invoked tasks 
11 
21 

embargo, está mostrando

Invoked tasks 
Invoking calculation for 20 
Invoking calculation for 10 
21 
11 

me gustaría aprender

  1. ¿Por qué las tareas no se ejecutan inmediatamente después de StartNew?
  2. ¿Qué debo hacer para obtener el resultado en el formato esperado?

Respuesta

10

Este es un resultado muy probable en una máquina con una CPU de un solo núcleo. O posible en uno con una CPU de varios núcleos y también está ocupado haciendo otra cosa.

La creación de una tarea o un subproceso solo configura una estructura lógica del sistema operativo que permite la ejecución del código. El programador del sistema operativo no no inmediatamente comienza a ejecutarlo si los núcleos están ocupados, el hilo tiene que competir con todos los otros hilos que se están ejecutando en la máquina. Una sesión típica de Windows tiene mil o más. 64 veces por segundo, el kernel genera una interrupción y el planificador reevalúa lo que está sucediendo para ver si otro hilo debe dar un giro. Cualquier subproceso que no esté bloqueando (esperando algún otro subproceso para realizar un trabajo como leer un archivo o paquete de red) es elegible para ejecutarse y el planificador elige el que tiene la más alta prioridad. Algún código adicional en el programador juega con los valores de prioridad para asegurar que todos los hilos tengan una oportunidad.

Oportunidad es la palabra clave aquí. La programación de subprocesos es no determinista.

Tenga en cuenta que nunca dije nada sobre la clase Thread o Task o ThreadPool. No tienen el poder de hacer gran cosa sobre la forma en que el sistema operativo programa los hilos. Todo lo que es posible es evitar que se ejecute un hilo, el trabajo del programador del grupo de subprocesos.

La prioridad importa, puede modificar la propiedad Thread.Priority o Task.Priority para afectar el resultado. Pero no slamdunk, el programador del sistema operativo ajusta constantemente una prioridad de subprocesos desde la prioridad base que establece con esa propiedad. No puede evitar que un hilo alguna vez se ejecute teniendo otro con una prioridad más alta, por ejemplo.

Esperar que los hilos se ejecuten en un orden predecible provoca el peor tipo de error, un error de raza enhebrado. El segundo peor es el punto muerto. Son extremadamente difíciles de depurar porque dependen del tiempo y los recursos de la máquina disponibles y la carga. Solo puede asegurarse de obtener un pedido en particular escribiendo un código que lo solucione explícitamente. Lo cual haces usando una primitiva de subprocesamiento como Mutex o la palabra clave . También es notable que cuando intente agregar dicho código a su fragmento, terminará con un programa que ya no tiene concurrencia. O en otras palabras, terminarás sin tener ningún uso para una Tarea. Un hilo o tarea solo será útil si puede permitirse que se ejecute en momentos impredecibles.

+0

+1 para indicar "La programación de subprocesos no es determinista". – Ramesh

7

¿Por qué las tareas no se ejecutan inmediatamente después de StartNew?

En cuanto MSDNStartNew() se horario una tarea para su ejecución.

Calling StartNew es funcionalmente equivalente a la creación de una tarea a través uno de sus constructores y luego llamar a Start para programarlo para ejecución. Sin embargo, a menos que la creación y la programación deban separarse, StartNew es el enfoque recomendado para la simplicidad y el rendimiento de .

Dado que TPL utiliza subprocesos de ThreadPool, a veces tiene que trabajar un poco para reservar e iniciar un subproceso de ThreadPool para la ejecución de una tarea en particular. Si necesita ejecutar explícitamente un subproceso separado sin ningún mecanismo intermedio como TaskScheduler de TPL, cree e inicie un subproceso de forma manual y obviamente no tendrá cosas tan claras como la continuación.

0

Tenga en cuenta que nunca he dicho nada acerca de la tarea ThreadPool o ThreadPool . No tienen el poder de hacer gran cosa acerca de la forma en que el sistema operativo programa los hilos. Todo lo que es posible es evitar que se ejecute un hilo, el trabajo del programador del grupo de subprocesos.

Guerra de ... Tareas vs Hilos ... Es muy dependiente de la tarea requerida.

Para la muestra, necesitamos cargar 100 imágenes de Internet con 100 tareas simultáneas (una tarea para un cliente) para hacer mosaicos de mapas para 100 clientes (un mosaico para un cliente). Y tenemos un límite de tiempo común, y un tiempo de carga de algunas tareas de carga puede coincidir con el límite de tiempo común. La prueba simple muestra que, la ejecución simultánea de 100 subprocesos (hilo de clase) durante un tiempo limitado, mucho más productivo, que la ejecución de 100 tareas (clase de tarea). El mismo resultado para 10 hilos frente a 10 tareas. Quiero decir que si necesitamos más que tareas "algo lentas" pero robustas, es decir, hacer mucho más trabajo en tareas simultáneas, entonces deberíamos usar el hilo de la clase.

Cuestiones relacionadas