2012-10-08 26 views
10

Estoy investigando el Parallelismo Break en un bucle For.Parallel.For and Break() malentendido?

Después de leer this y this todavía tengo una pregunta:

yo esperaría que este código:

Parallel.For(0, 10, (i,state) => 
    { 
       Console.WriteLine(i); if (i == 5) state.Break(); 
    } 

para producir al la mayoría de 6 números (0..6). no sólo no lo está haciendo, pero tienen diferente longitud del resultado:

02351486 
013542 
0135642 

muy molesto. (¿Dónde demonios está Break() {5} después de aquí ??)

Así que miré a MSDN

rotura puede ser utilizado para comunicarse con el bucle que no hay otras iteraciones después de la iteración actual tienen que ser correr. Si se invoca Break desde la centésima iteración de un bucle for que itera en paralelo de 0 a 1000, todas las iteraciones menores de 100 deben seguir ejecutándose , pero las iteraciones de 101 a 1000 no son necesarias.

quesion # 1:

Qué iteraciones? el contador de iteración general? o por hilo? Estoy bastante seguro de que es por hilo. por favor apruébalo.

Pregunta # 2:

supongamos que estamos utilizando partición Parallel gama + (debido a ningún cambio costo de CPU entre los elementos), de modo que divide los datos entre los hilos. Así que si tenemos 4 núcleos (y divisiones perfectos entre ellos):

core #1 got 0..250 
core #2 got 251..500 
core #3 got 501..750 
core #4 got 751..1000 

modo que el hilo en core #1 se reunirán value=100 en algún momento y se romperá. este será su número de iteración 100. Pero el hilo en core #4 tiene más cuantos y ahora está en 900. él es más allá de su 100'th iteración. ¡No tiene índice menos de 100 para detenerse! - para que él los muestre a todos.

¿No es así? ¿Es esa la razón por la que obtengo más de 5 elementos en mi ejemplo?

Pregunta # 3:

Cómo cn que realmente se rompen cuando (i == 5)?

p.s.

Quiero decir, vamos! cuando hago Break(), quiero que el loop se detenga. exactamente como lo hago en el lazo normal For.

Respuesta

10

Para dar como máximo 6 números (0..6).

El problema es que esto no rendimiento en la mayoría de 6 números.

Lo que sucede es que cuando se pulsa un ciclo con un índice de 5, se envía la solicitud de "interrupción". Break() hará que el ciclo ya no procese ningún valor >5, pero procesará todos los valores <5.

Sin embargo, cualquier valor superior a 5 que ya se haya iniciado seguirá procesándose. Dado que los distintos índices se ejecutan en paralelo, ya no se ordenan, por lo que se obtienen varias ejecuciones donde todavía se están ejecutando algunos valores >5 (como 8 en su ejemplo).

¿Qué iteraciones? el contador de iteración general? o por hilo? Estoy bastante seguro de que es por hilo. por favor apruébalo.

Este es el índice que se pasa a Paralelo.Para. Break() no se evitará que se procesen los artículos, pero ofrece una garantía de que todos los artículos de hasta 100 se procesen, pero los artículos de más de 100 pueden procesarse o no.

¿No es así? ¿Es esa la razón por la que obtengo más de 5 elementos en mi ejemplo?

Sí. Si usa un particionador como el que ha mostrado, tan pronto como llame al Break(), los elementos que se encuentren más allá del lugar donde se rompe ya no se programarán. Sin embargo, los elementos (que es la partición completa ) ya programados se procesarán por completo. En su ejemplo, esto significa que es probable que siempre procese los 1000 elementos.

¿Cómo puedo realmente romper cuando (i == 5)?

que son - pero cuando se ejecuta en paralelo, las cosas cambian.¿Cuál es el objetivo real actual aquí? Si solo desea procesar los primeros 6 elementos (0-5), debe restringir los elementos antes de y recorrerlos mediante una consulta LINQ o similar. A continuación, puede procesar los 6 elementos en Parallel.For o Parallel.ForEach sin un Break() y sin preocupaciones.

Quiero decir, vamos! cuando hago Break(), quiero que el loop se detenga. exactamente como lo hago en el ciclo For normal.

Debe utilizar Stop() en lugar de Break() si quieres que las cosas se detienen tan pronto como sea posible. Esto no evitará que los artículos que ya están en ejecución se detengan, pero ya no programará ningún elemento (incluidos los que estén en índices más bajos o antes en la enumeración que su posición actual).

+0

quiere decir que _Break() hará que el bucle ya no procese ningún valor que su INDICE> 5_ .... ¿verdad? –

+0

@RoyiNamir Sí, más o menos. Los elementos seguirán procesándose si ya se han programado y comenzado, pero no se procesarán nuevos elementos. Si se encuentra en 'Parallel.ForEach', los elementos más allá de la ubicación en la enumeración a la que llamará Break() ya no se programarán. –

+0

¿hay alguna diferencia en 'for vs foreach' con respecto al descanso ??? –

6

si la interrupción es llamado desde la iteración 100a de un para la iteración de bucle en paralelo desde 0 a 1000

La iteración 100a del bucle no es necesariamente (de hecho probablemente no) el uno con el índice 99.

Sus hilos pueden y se ejecutarán en una orden indeterminada. Cuando se encuentra la instrucción .Break(), no se iniciarán más iteraciones de bucle. Exactamente cuando eso sucede depende de los detalles de la programación de hilos para una ejecución en particular.

recomiendo encarecidamente la lectura de

Patterns of Parallel Programming

(PDF gratuito de Microsoft)

para entender las decisiones de diseño y soluciones de compromiso de diseño que entraron en el TPL.

4

¿Qué iteraciones? el contador de iteración general? o por hilo?

Apagado de todas las iteraciones programadas (o aún por programar).

Recuerde que el delegado puede estar fuera de servicio, no hay garantía de que la iteración i == 5 sea la sexta en ejecutarse, pero es poco probable que sea así, excepto en casos excepcionales.

Q2: Am I right?

No, la programación no es tan simple. Más bien, todas las tareas se ponen en cola y luego se procesa la cola. Pero cada uno de los subprocesos usa su propia cola hasta que esté vacía cuando roban de otros hilos. Esto no permite predecir qué hilo procesará qué delegado.

Si los delegados son lo suficientemente triviales, todo podría procesarse en el hilo de llamada original (ningún otro hilo tiene la oportunidad de robar el trabajo).

Q3: ¿Cómo puedo realmente romper cuando (i == 5)?

No lo use al mismo tiempo si desea procesamiento lineal (en específico).

El método Break está ahí para respaldar la ejecución especulativa: pruebe varias formas y deténgase tan pronto como se complete una.

+0

* No lo use al mismo tiempo si desea procesamiento lineal (en específico). * ... verdadero, pero ... uno puede asegurarse de que una sección del trabajo ocurra en paralelo, el trabajo esté sincronizado en un punto requerido, luego el trabajo adicional se haga en paralelo hasta otro punto de sincronización requerido, etc. Solo quiero enfatizar que su declaración correcta deja espacio para ese tipo de patrón . –

+0

@EricJ. Es cierto, pero no hay evidencia en la pregunta de que este sea un punto de sincronización entre otras dos piezas de trabajo (y 'Parallel.For 'a menudo sería el punto de partida incorrecto para eso). Más bien, Q es una falta significativa de comprensión de que 'Parallel.For' no es un" ciclo de bucle con magia extra ". – Richard