2012-05-24 29 views
6

¿Hay algún cambio que un Trabajador en segundo plano funcione mejor que Tareas en 5 segundos procesos en ejecución? Recuerdo haber leído en un libro que una Tarea está diseñada para procesos de ejecución corta.tarea fondo trabajador C#

El reasong lo que pido es esto:

Tengo un proceso que tarda 5 segundos para completar, y hay 4000 para completar los procesos. Al principio lo hice:

for (int i=0; i<4000; i++) { 
    Task.Factory.StartNewTask(action); 
} 

y esto tuvo un bajo rendimiento (después del primer minuto, 3-4 tareas en las que completada, y la aplicación de consola tenía 35 hilos). Tal vez esto fue estúpido, pero pensé que el grupo de subprocesos manejaría este tipo de situación (pondrá todas las acciones en una cola, y cuando un subproceso es libre, tomará una acción y lo ejecutará).

El segundo paso ahora era hacer manualmente los trabajadores de segundo plano Environment.ProcessorCount, y todas las acciones que se colocarán en un ConcurentQueue. Así que el código sería algo como esto:

var workers = new List<BackgroundWorker>(); 
//initialize workers 

workers.ForEach((bk) => 
{ 
    bk.DoWork += (s, e) => 
    { 
     while (toDoActions.Count > 0) 
     { 
      Action a; 
      if (toDoActions.TryDequeue(out a)) 
      { 
       a(); 
      } 
     } 
    } 

    bk.RunWorkerAsync(); 
}); 

Esto se realizó de una manera mejor. Funcionó mucho mejor que las tareas incluso cuando tenía 30 trabajadores en segundo plano (tantas tareas como en el primer caso).

LE:

que iniciar las tareas de esta manera:

public static Task IndexFile(string file) 
    { 
     Action<object> indexAction = new Action<object>((f) => 
     { 
      Index((string)f); 
     }); 

     return Task.Factory.StartNew(indexAction, file); 
    } 

Y el método del índice es la siguiente:

private static void Index(string file) 
    { 
     AudioDetectionServiceReference.AudioDetectionServiceClient client = new AudioDetectionServiceReference.AudioDetectionServiceClient(); 

     client.IndexCompleted += (s, e) => 
      { 
       if (e.Error != null) 
       { 
        if (FileError != null) 
        { 
         FileError(client, 
          new FileIndexErrorEventArgs((string)e.UserState, e.Error)); 
        } 
       } 
       else 
       { 
        if (FileIndexed != null) 
        { 
         FileIndexed(client, new FileIndexedEventArgs((string)e.UserState)); 
        } 
       } 
      }; 

     using (IAudio proxy = new BassProxy()) 
     { 
      List<int> max = new List<int>(); 
      if (proxy.ReadFFTData(file, out max)) 
      { 
       while (max.Count > 0 && max.First() == 0) 
       { 
        max.RemoveAt(0); 
       } 
       while (max.Count > 0 && max.Last() == 0) 
       { 
        max.RemoveAt(max.Count - 1); 
       } 

       client.IndexAsync(max.ToArray(), file, file); 
      } 
      else 
      { 
       throw new CouldNotIndexException(file, "The audio proxy did not return any data for this file."); 
      } 
     } 
    } 

Este método lee de un mp3 presentar algunos datos, utilizando la biblioteca Bass.net. Luego, esos datos se envían a un servicio WCF, utilizando el método async. El método IndexFile (archivo de cadena), que crea tareas, se invoca 4000 veces en un ciclo for. Esos dos eventos, FileIndexed y FileError no se manejan, por lo que nunca se lanzan.

+0

Es posible que desee utilizar 'BlockingCollection' en lugar de' ConcurrentQueue' (usará 'ConcurrentQueue' internamente). Hará que el código sea un poco más limpio y fácil de usar. – Servy

+0

Gracias por la sugerencia ... Voy a cambiar :) –

+0

¿Has probado 'Parallel.Invoke' con una variedad de acciones? – mellamokb

Respuesta

1

La razón por la que el rendimiento de las tareas fue tan bajo fue porque montó demasiadas tareas pequeñas (4000). Recuerde que la CPU necesita programar las tareas también, por lo que montar muchas tareas breves genera una carga de trabajo adicional para la CPU. Más información se puede encontrar en el segundo párrafo del TPL:

A partir de .NET Framework 4, el TPL es la manera preferida de multiproceso de escritura y código paralelo. Sin embargo, no todo el código es adecuado para la paralelización; por ejemplo, si un ciclo solo realiza una pequeña cantidad de trabajo en cada iteración, o no se ejecuta para muchas iteraciones de , la sobrecarga de la paralelización puede hacer que el código se ejecute más lentamente.

Cuando se utiliza los trabajadores de fondo, que limita el número de posibles hilos vivos a la ProcessCount. Lo cual redujo una gran cantidad de gastos generales de programación.

0

¿Ha considerado usar threadpool?

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx

Si su rendimiento es más lento cuando se utilizan hilos, que sólo puede ser debido a la sobrecarga de roscar (asignación y la destrucción de hilos individuales).

+0

Bueno, ¿las tareas se recuperaron de ThreadPool? Estoy creando objetos Task, no Thread. –

+0

Él ya estaba usando el TP, esa fue la queja. –

+1

La tarea usa threadpool. –

1

Dado que tiene una lista estrictamente definida de cosas que hacer, usaría la clase Parallel (For o ForEach dependiendo de lo que más le convenga). Además se puede pasar un parámetro de configuración para cualquiera de estos métodos para controlar el número de tareas realmente se lleva a cabo al mismo tiempo:

 System.Threading.Tasks.Parallel.For(0, 20000, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, i => 
     { 
      //do something 
     }); 

El código anterior se presentará 20000 operaciones, pero no llevará a cabo más de 5 operaciones en el Mismo tiempo.

Sospecho que la razón de fondo de los trabajadores les fue mejor para usted era porque habías los creó y crea una instancia en la salida, mientras que en el código de ejemplo Task parece que está creando un nuevo objeto Task para cada operación.

Como alternativa, ¿pensó en utilizar un número fijo de objetos Task instanciados al principio y luego realizar una acción similar con un ConcurrentQueue como lo hizo con los trabajadores de segundo plano? Eso también debería ser bastante eficiente.