Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

161
Vistas
¿Cómo sincronizar temporalmente en un bucle Parallel.ForEach?

Así que aquí hay una versión mínima de código que funciona, pero es ineficiente:

 Parallel.ForEach(list, x => { doThing1(x); }); Thing1Done = true; Parallel.ForEach(list, x => { doThing2(x); }); Thing2Done = true; Parallel.ForEach(list, x => { doThing3(x); }); Thing3Done = true;

Intuitivamente, me gustaría ejecutar las 3 "cosas" dentro del mismo bucle, pero deben poder sincronizarse temporalmente para actualizar la propiedad Thing*n*Done respectiva.

Pseudocódigo para esta idea de la siguiente manera:

 Parallel.ForEach(list, x => { doThing1(x); // wait for doThing1 to be completed for all other elements in list Thing1Done = true; doThing2(x); // wait for doThing2 to be completed for all other elements in list Thing2Done = true; doThing3(x); // wait for doThing3 to be completed for all other elements in list Thing3Done = true; });

Entonces, por ejemplo, es necesario que doThing1() finalice su ejecución para cada miembro de la list antes de que Thing1Done se establezca en verdadero. doThing2() solo puede comenzar después de que se haya configurado Thing1Done .

Cada paso individual no es demasiado costoso, y me preocupan los gastos generales relacionados con el enfoque ingenuo. ¿Cuál es la mejor manera de resolver esta tarea de manera eficiente , suponiendo que la sobrecarga involucrada en la inicialización de los subprocesos es mi mayor preocupación? También me gustaría evitar la espera ocupada (donde el hilo gira en un ciclo while sin hacer nada útil hasta que alguna bandera se establece en verdadero) si es posible.

Estoy dispuesto a escribir algo más general si la biblioteca paralela no puede hacer lo que quiero.

over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

Podría usar la primitiva de sincronización de subprocesos de Barrier :

 var barrier = new Barrier(list.Count); var options = new ParallelOptions() { MaxDegreeOfParallelism = list.Count, TaskScheduler = new ThreadPerTask() }; Parallel.ForEach(list, options, x => { doThing1(x); // wait for doThing1 to be completed for all other elements in list barrier.SignalAndWait(); doThing2(x); // wait for doThing2 to be completed for all other elements in list barrier.SignalAndWait(); doThing3(x); // wait for doThing3 to be completed for all other elements in list barrier.SignalAndWait(); });

El método SignalAndWait señala la finalización de un elemento y espera hasta que todos los elementos se hayan completado (hasta que la propiedad ParticipantsRemaining sea cero). Después de que se incremente la propiedad CurrentPhaseNumber , todos los subprocesos se desbloquean simultáneamente y son libres de correr hacia el siguiente hito.

Esta es una forma extremadamente ineficiente de procesar los elementos de la list , ya que requiere un subproceso dedicado por elemento. Necesitará un TaskScheduler personalizado para implementar esta configuración, como la que se muestra a continuación:

 public class ThreadPerTask : TaskScheduler { protected override void QueueTask(Task task) { new Thread(() => this.TryExecuteTask(task)) { IsBackground = true }.Start(); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => false; protected override IEnumerable<Task> GetScheduledTasks() { yield break; } }

En mi humilde opinión, su primer enfoque, el que usa múltiples bucles Parallel.ForEach consecutivos, es la forma correcta de resolver este problema.

over 4 years ago · Santiago Trujillo Denunciar

0

Como se mencionó en los comentarios, su segundo ejemplo tendrá una sobrecarga enorme, ya que requeriría usar un subproceso por elemento, y probablemente serán más subprocesos de los que están disponibles en el grupo de subprocesos, por lo que será necesario crear nuevos subprocesos, pero la tasa nueva los hilos que se crean son limitados. Así que esperaría un rendimiento terrible. Al menos suponiendo que tenga muchos elementos para procesar.

Si los requisitos son que doThing1 debe haberse completado para todos los elementos antes de que comience doThing2 , entonces no creo que pueda hacerlo mucho mejor que múltiples bucles paralelos secuenciales. No esperaría que la sobrecarga de este enfoque fuera tan mala, ya que utilizará el grupo de subprocesos en lugar de generar nuevos subprocesos.

Es posible que un particionador personalizado pueda ayudar, o dividir previamente su lista en fragmentos y procesar cada fragmento en paralelo. Por defecto, los bucles paralelos intentan dividir el trabajo en particiones e intentan adaptar el tamaño de estas particiones para equilibrar la sobrecarga con la utilización del proceso. Pero si tiene conocimiento previo del tipo de trabajo que debe hacer, probablemente pueda hacerlo mejor usted mismo. Como siempre cuando se habla de rendimiento, probablemente necesitará medir para ver qué alternativa es más rápida.

Si divide el trabajo en partes similares a la cantidad de procesadores, tal vez podría usar un modelo similar a su segundo ejemplo. Pero no estoy seguro de si tiene alguna ventaja real.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda