2010-02-19 28 views
5

Digamos que tengo dos matrices:acceso a la matriz de múltiples hilos

int[] array1 = new int[2000000]; 
int[] array2 = new int[2000000]; 

me quedo algunos valores en los arrays y luego desea agregar el contenido de matriz2 a Array1 así:

for(int i = 0; i < 2000000; ++i) array1[i] += array2[i]; 

Ahora, digamos que quiero acelerar el procesamiento en una máquina multiprocesador, así que en lugar de hacer un ciclo como el anterior, creo dos hilos. Uno de los cuales he procesado los primeros 1000000 elementos en la matriz, el otro he procesado los últimos 1000000 elementos en la matriz. Mi hilo principal espera que esos dos hilos notifiquen que están terminados, y luego procede a usar los valores de array1 para todo tipo de cosas importantes. (Tenga en cuenta que los dos hilos de trabajo no pueden terminarse y pueden reutilizarse, pero el hilo principal no se reanudará hasta que ambos lo hayan notificado).

Entonces, mi pregunta es: ¿cómo puedo estar seguro de que el hilo principal verá las modificaciones que los dos hilos de trabajo hicieron a la matriz? ¿Puedo contar con que esto suceda o necesito pasar por algún procedimiento especial para asegurarme de que los subprocesos de trabajo descarguen sus escrituras en la matriz y que el hilo principal descarte los valores de la matriz en caché?

Respuesta

2

Usted necesita una barrera de memoria para asegurarse de que el subproceso de trabajo se escribe en la matriz son visibles para el hilo principal en el orden que usted espera.

Si necesita o no una barrera de memoria explícita depende de cómo notifique al hilo principal. Esperar en la mayoría de las primitivas de sincronización, como los eventos, proporciona una barrera implícita, por lo que no se requieren cambios de su parte. Sondear una variable global no proporciona una barrera.

Si necesita una barrera explícita, use Thread.MemoryBarrie r.

+2

Dado que planea bloquear el hilo principal (según la descripción), la barrera de memoria no debería ser necesaria. –

+0

No dijo explícitamente cómo estaba esperando (podría pasar un tiempo (! Done) {}). Cualquier forma sensata de esperar no requerirá una barrera. – Michael

+0

Estoy usando objetos ManualResetEvent para dejar que el hilo principal señale los hilos del trabajador y viceversa. Tengo curiosidad por la funcionalidad de MemoryBarrier, aunque he estado trabajando en esto. Los documentos de MS dicen muy poco al respecto y básicamente dicen que evita el reordenamiento de las instrucciones. me dio la impresión de que enrojeció/borra la memoria caché del hilo actual, pero nunca se sabía a ciencia cierta. (Si es así, incluso sin la sincronización de hilos real, los trabajadores podrían llamar MemoryBarrier y luego, si el hilo principal llamado MemoryBarrier después, se verían los cambios en la matriz, ¿verdad?) – nonoitall

0

Probablemente estarás bien si estás utilizando devoluciones de llamada cuando hayan terminado de modificar las matrices, pero si hay alguna pregunta utilizando un Bloqueo, se asegurará de que los otros hilos hayan soltado las matrices.

lock (array1) 
{ 
} 

http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.71).aspx

+1

bloqueo toda la matriz para la duración de la operación elimina la capacidad de las roscas para trabajar en paralelo. – Michael

+1

Tampoco es necesario. –

+0

Un bloqueo garantiza que ninguna otra hebra tenga la matriz bloqueada cuando una hebra determinada la ingrese; no garantiza que otras hebras estén abandonando su bloqueo. – JoshJordan

0

Siempre y cuando no hayas hecho una copia de la matriz en el hilo principal, no creo que debas hacer nada. Solo espere hasta que los hilos de trabajo hayan terminado.

2

¿Cómo puedo estar seguro de que el hilo principal verá las modificaciones que los dos hilos de trabajo hicieron en la matriz? ¿Puedo contar con que esto suceda o necesito pasar por algún procedimiento especial para asegurarme de que los subprocesos de trabajo descarguen sus escrituras en la matriz y que el hilo principal descarte los valores de la matriz en caché?

Aquí no necesita ningún tratamiento especial: siempre trabajará con los mismos objetos en la memoria.

Además, dado que cada subproceso funcionará en una parte separada de la matriz, no es necesario el bloqueo.

Sin embargo, si solo está haciendo una simple adición, la sobrecarga de enhebrar y sincronizar de nuevo al hilo principal ~ podría ~ superar los beneficios obtenidos ... Si hace esto, perfile para asegurarse de que está proporcionando un ganancia neta.

+0

Sí - la operación real es un poco más compleja que la adición (se acaba usando eso como un ejemplo) pero es la misma porque el elemento n de array1 se actualiza en base a sí mismo y el enésimo elemento de matriz2. Perfilar en un par de matrices de un tamaño decente muestra casi el doble de rendimiento cuando se pasa de un hilo a dos en una máquina de doble núcleo, así que estoy bastante seguro de que vale la pena. Solo quiero asegurarme de estar haciéndolo correctamente. :-) – nonoitall

+0

@nonoitall: siempre que siempre use el elemento n-ésimo en un único subproceso a la vez, no debería ser un problema. Me alegra saber que vale la pena el esfuerzo! –

1

Si se rompe hasta el rango de índices en rangos que no se solapan como usted sugiere, a continuación, siempre que la matriz se crea en la memoria compartida (es decir, no por cada hilo), entonces no se requieren esclusas.

5

Si tienes suerte y tienen la opción de utilizar .NET 4.0 a continuación, puedes escribir:

Parallel.For(0, 2000000, i => { array1[i] += array2[i]; }); 

que no es necesario ningún bloqueo explícita o sincronización, debido a que:

  • Cada tarea (ejecución del cuerpo de bucles for) afecta a la parte disjunta de la matriz
  • Parallel.For espera hasta que todas las tareas finalicen antes de que vuelva, por lo que habrá una barrera de memoria implícita.
+0

¡Eso es increíble! No me di cuenta de que estaban agregando eso a .NET 4.0. ¿Es compatible con Mono aún por casualidad? Una búsqueda – nonoitall

+0

qucik muestra que existen algunos intentos para apoyar cosas paralelo en .NET 4.0 en Mono: http://tirania.org/blog/archive/2008/Jul-26-1.html. No estoy seguro de cuál es el estado actual, aunque ... –

+1

Lo probé, pero parece Paralelo. Solo se realiza un poco más rápido que usando un solo hilo.Dado que mi operación de "adición" es bastante rápida, asumo que la velocidad no impresionante viene de la sobrecarga oculta de llamar a un delegado por cada iteración de bucle. Es una lástima que CLR no pueda alinear al delegado en el tiempo de ejecución. Supongo que podría desenrollar el ciclo para acelerarlo, pero creo que me quedaré con el método manual por ahora. – nonoitall

Cuestiones relacionadas