2008-09-19 18 views
13

Imagine que tengo una función que pasa por un millón/billón de cadenas y comprueba a la vez en ellas.Acelerar el bucle utilizando subprocesamiento múltiple en C# (Pregunta)

f.ex:

foreach (String item in ListOfStrings) 
{ 
    result.add(CalculateSmth(item)); 
} 

que consume gran cantidad de tiempo, porque es muy CalculateSmth función de tiempo que consume.

Quiero preguntar: ¿cómo integrar multihilo en este proceso?

f.ex: Quiero encender 5 hilos y cada uno de ellos arroja algunos resultados, y eso continúa hasta que la lista tenga elementos.

Tal vez alguien puede mostrar algunos ejemplos o artículos ..

se olvidó de mencionar lo necesito en .NET 2.0

+0

¿Necesita los resultados de vuelta en el mismo orden? – Keith

+0

¿Podría usar varios trabajadores de fondo? crear algún tipo de lógica que llevaría el recuento de lista de cadenas a continuación, crear una cantidad X de BWs y divy componen cada uno – Crash893

Respuesta

17

Usted podría intentar la Parallel extensions (parte de .NET 4.0)

Estos permiten a escribir algo como:

Parallel.Foreach (ListOfStrings, (item) => 
    result.add(CalculateSmth(item)); 
); 

de result.add supuesto tendría que ser seguro para subprocesos.

+0

en este caso, habrá alguna condición de carrera en la colección de resultados? después de todos múltiples hilos puede estar ejecutando simultáneamente result.add ... – cruizer

+0

result.add debe ser seguro para subprocesos sí .. – Tobi

+0

se olvidó de mencionar lo necesito en .NET 2.0 –

1

No es que tenga buenos artículos aquí en este momento, pero lo que quiere hacer es algo a lo largo de Producer-Consumer con Threadpool.

The Producers recorre y crea tareas (que en este caso podría ser simplemente poner en cola los elementos en una lista o pila). Los consumidores son, por ejemplo, cinco hilos que leen un elemento de la pila, lo consumen al calcularlo y luego lo almacenan en otro lugar.

De esta manera, el multihebra está limitado a solo esos cinco hilos, y todos tendrán que trabajar hasta que la pila esté vacía.

Cosas para pensar:

  • protección colocado en la lista de entrada y de salida, como un mutex.
  • Si la orden es importante, asegúrese de que la orden de salida se mantenga. Un ejemplo podría ser almacenarlos en SortedList o algo así.
  • Asegúrese de que CalculateSmth es seguro para subprocesos, que no utiliza ningún estado global.
2

La primera pregunta que debe responder es si usted debe utilizar el roscado

Si su CalculateSmth() la función es básicamente vinculado a la CPU, es decir pesada en uso de la CPU y, básicamente, hay E/S en el uso, entonces me es difícil ver el punto de usar hilos, ya que los hilos competirán por el mismo recurso, en este caso la CPU.

Si su CalculateSmth() está utilizando tanto CPU como E/S, entonces podría ser un punto en el uso del enhebrado.

Estoy totalmente de acuerdo con el comentario de mi respuesta. Hice una suposición errónea de que estábamos hablando de una sola CPU con un núcleo, pero en estos días tenemos CPUs multi-core, mi mal.

+1

depende de si se trata de un sistema multi-núcleo. Si tiene cuatro núcleos disponibles, por ejemplo, el uso de cuatro subprocesos debería tener una aceleración aproximada de cuatro veces en el procesamiento (suponiendo que no haya interdependencias entre los subprocesos). –

18

Las extensiones paralelas es fresco, pero esto también se puede hacer sólo mediante el uso de subprocesos de la siguiente manera:

using System.Collections.Generic; 
using System.Threading; 

namespace noocyte.Threading 
{ 
    class CalcState 
    { 
     public CalcState(ManualResetEvent reset, string input) { 
      Reset = reset; 
      Input = input; 
     } 
     public ManualResetEvent Reset { get; private set; } 
     public string Input { get; set; } 
    } 

    class CalculateMT 
    { 
     List<string> result = new List<string>(); 
     List<ManualResetEvent> events = new List<ManualResetEvent>(); 

     private void Calc() { 
      List<string> aList = new List<string>(); 
      aList.Add("test"); 

      foreach (var item in aList) 
      { 
       CalcState cs = new CalcState(new ManualResetEvent(false), item); 
       events.Add(cs.Reset); 
       ThreadPool.QueueUserWorkItem(new WaitCallback(Calculate), cs); 
      } 
      WaitHandle.WaitAll(events.ToArray()); 
     } 

     private void Calculate(object s) 
     { 
      CalcState cs = s as CalcState; 
      cs.Reset.Set(); 
      result.Add(cs.Input); 
     } 
    } 
} 
+1

¿Y cómo sabes cuándo está terminado? mmm. – leppie

+0

Podría tener un evento ManualResetEvent que llama la función WaitCallback y el hilo principal WaitOne activado. –

+0

Se agregó un código para mostrar cómo puede usar MRE para hacer eso. – noocyte

12

Tenga en cuenta que la concurrencia no mágicamente darle más recursos. Necesita establecer lo que está disminuyendo CalculateSmth hacia abajo.

Por ejemplo, si está vinculado a CPU (y está en un solo núcleo), entonces el mismo número de ticks de CPU irá al código, ya sea que los ejecute secuencialmente o en paralelo. Además, obtendrás un poco de sobrecarga al administrar los hilos. El mismo argumento se aplica a otras restricciones (por ejemplo, E/S)

Solo obtendrá ganancias de rendimiento en esto si CalculateSmth deja libre de recursos durante su ejecución, que podría ser utilizado por otra instancia. Eso no es raro. Por ejemplo, si la tarea implica IO seguido de algunas cosas de la CPU, entonces el proceso 1 podría estar haciendo las cosas de la CPU mientras que el proceso 2 está haciendo la IO. Como señala mat, una cadena de unidades de productores y consumidores puede lograr esto, si tiene la infraestructura.

5

necesita dividir el trabajo que quiere hacer en paralelo. Aquí está un ejemplo de cómo se puede dividir el trabajo en dos:

List<string> work = (some list with lots of strings) 

// Split the work in two 
List<string> odd = new List<string>(); 
List<string> even = new List<string>(); 
for (int i = 0; i < work.Count; i++) 
{ 
    if (i % 2 == 0) 
    { 
     even.Add(work[i]); 
    } 
    else 
    { 
     odd.Add(work[i]); 
    } 
} 

// Set up to worker delegates 
List<Foo> oddResult = new List<Foo>(); 
Action oddWork = delegate { foreach (string item in odd) oddResult.Add(CalculateSmth(item)); }; 

List<Foo> evenResult = new List<Foo>(); 
Action evenWork = delegate { foreach (string item in even) evenResult.Add(CalculateSmth(item)); }; 

// Run two delegates asynchronously 
IAsyncResult evenHandle = evenWork.BeginInvoke(null, null); 
IAsyncResult oddHandle = oddWork.BeginInvoke(null, null); 

// Wait for both to finish 
evenWork.EndInvoke(evenHandle); 
oddWork.EndInvoke(oddHandle); 

// Merge the results from the two jobs 
List<Foo> allResults = new List<Foo>(); 
allResults.AddRange(oddResult); 
allResults.AddRange(evenResult); 

return allResults; 
Cuestiones relacionadas