Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

158
Views
¿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 answers
Answer question

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!