2011-12-11 32 views
5

¿Cuál sería la forma más efectiva de pausar y detener (antes de que termine) parallel.foreach?Parallel.Foreach C# Pausa y función de parada?

Parallel.ForEach(list, (item) => 
{ 
    doStuff(item); 
}); 
+5

Pausa? ¿Por qué querrías detenerlo? –

+0

Pausa es solo una característica adicional que quería agregar si es posible, por supuesto. Estoy escribiendo un programa que tiene mucha CPU y conexión a Internet y puede llenarlo rápidamente para que una pausa no sea tan mala. Voy a implementar la funcionalidad de detención y guardar la lista de elementos sin terminar en otra lista y luego en la siguiente ejecución reescribir esa lista. Y continuar desde donde está parado. Un poco como pausa Gracias. – bbrez1

+0

Si una de las respuestas le dio una solución [debe marcar esa respuesta como aceptada] (http://stackoverflow.com/faq#howtoask). –

Respuesta

11

Damien_The_Unbeliver tiene un buen método, pero eso es solo si desea tener algún proceso externo para detener el ciclo. Si desea que el bucle salga como si fuera break en un bucle normal for o foreach, necesitará usar a overload que tenga un ParallelLoopState como uno de los parámetros del cuerpo del bucle. ParallelLoopState tiene dos funciones que son relevantes para lo que quiere hacer, Stop() y Break().

La función Stop() dejará de elementos de procesamiento a la mayor brevedad posible del sistema de que significa que más iteraciones se podrían realizar después de llamar a la parada() y no se garantiza que los elementos que vinieron antes que el elemento que se detuvo en incluso han comenzado para procesar.

La función Break() realiza exactamente igual que Stop() sin embargo, también evaluará todos los elementos del IEnumerable que vinieron antes del elemento al que se llamó en Break(). Esto es útil para cuando no le importa en qué orden se procesan los elementos, pero debe procesar todos los elementos hasta el punto que detuvo.

Inspeccione el ParallelLoopResult devuelto desde el foreach para ver si el foreach se detuvo antes, y si usó Break(), ¿cuál es el artículo con el número más bajo que procesó?

Parallel.ForEach(list, (item, loopState) => 
    { 
     bool endEarly = doStuff(item); 
     if(endEarly) 
     { 
      loopState.Break(); 
     } 
    } 
    ); 
//Equivalent to the following non parallel version, except that if doStuff ends early 
// it may or may not processed some items in the list after the break. 
foreach(var item in list) 
{ 
    bool endEarly = doStuff(item); 
    if(endEarly) 
    { 
     break; 
    } 
} 

Aquí es un ejemplo más práctico

static bool[] list = new int[]{false, false, true, false, true, false}; 

long LowestElementTrue() 
{ 
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState) => 
    { 
     if(element) 
      loopState.Break(); 
    } 
    if(result.LowestBreakIteration.IsNull) 
     return -1; 
    else 
     return result.LowestBreakIteration.Value; 
} 

No importa la forma en que se divide el trabajo que siempre devolverá 2 como respuesta.

Digamos que el procesador distribuye dos hilos para procesar esto, el primer hilo procesa los elementos 0-2 y el segundo hilo procesa los elementos 3-5.

 
Thread 1:    Thread 2 
0, False, continue next 3, False, continue next 
1, False, continue next 4, True, Break 
2, True, Break   5, Don't process Broke 

Ahora la rotura menor índice fue llamado desde 2 era tan ParallelLoopResult.LowestBreakIteration devolverá 2 cada vez, no importa cuan-los hilos se rompen, ya que siempre se procesará hasta el número 2.

Aquí un ejemplo de cómo podría usarse Stop.

static bool[] list = new int[]{false, false, true, false, true, false}; 

long FirstElementFoundTrue() 
{ 
    long currentIndex = -1; 
    ParallelLoopResult result = Parallel.ForEach(list, (element, loopState, index) => 
    { 
     if(element) 
     { 
      loopState.Stop(); 

      //index is a 64 bit number, to make it a atomic write 
      // on 32 bit machines you must either: 
      // 1. Target 64 bit only and not allow 32 bit machines. 
      // 2. Cast the number to 32 bit. 
      // 3. Use one of the Interlocked methods. 
      Interlocked.Exchange (ref currentIndex , index); 
     } 
    } 
    return currentIndex; 
} 

Según cómo se divida el trabajo, devolverá 2 o 4 como respuesta.

Digamos que el procesador distribuye dos hilos para procesar esto, el primer hilo procesa los elementos 0-2 y el segundo hilo procesa los elementos 3-5.

 
Thread 1:     Thread 2 
0, False, continue next 3, False, continue next 
1, False, continue next 4, True, Stop 
2, Don't process, Stopped 5, Don't process, Stopped 

En este caso, devolverá 4 como respuesta. Veamos el mismo proceso pero si procesa cualquier otro elemento en lugar de 0-2 y 3-5.

 
Thread 1:     Thread 2 
0, False, continue next  1, False, continue next 
2, True, Stop    3, False, continue next 
4, Don't process, Stopped 5, Don't process, Stopped 

Esta vez se volverá 2 en lugar de 4.

2

Para ser capaz de detener un Parallel.ForEach, puede utilizar una de las sobrecargas que acepta un parámetro ParallelOptions, e incluir una CancellationToken en esas opciones.

Consulte Cancellation para obtener más información.

En cuanto a pausando, no puedo pensar por qué querrías hacer eso, en general. Es posible que esté buscando un Barrier (que se utiliza para coordinar esfuerzos entre varios hilos, por ejemplo, si todos deben completar la parte A antes de continuar con la parte B), pero no creo que lo use con Parallel.ForEach, ya que usted no sabe cuántos participantes habrá.