2011-08-15 10 views
8

Tengo un Parallel.ForEach ejecutándose dentro de una Tarea. Se itera sobre una colección de direcciones de correo electrónico y envía un MailMessage a la cola SMTP, una vez que se envía actualiza una tabla en el DB con un resultado.Parallel.ForEach iterando elementos en la colección varias veces

Puedo ver en la base de datos que está enviando el MailMessage a la cola varias veces, a veces hasta 6 veces. Aquí está mi código simplificado, ¿alguien puede recomendar un mejor enfoque?

El botón de clic, que crea una nueva tarea ...

CampaignManager.Broadcast.BroadcastService broadcastService = new CampaignManager.Broadcast.BroadcastService(); 

     var task = Task<CampaignManager.Broadcast.Results.Broadcast>.Factory.StartNew(() => { 
      return broadcastService.BroadcastCampaign(); 
     }, TaskCreationOptions.LongRunning); 

     Task.WaitAny(task); 

     if (task.Result != null) 
     { 
      Broadcast.Results.Broadcast broadcastResult = task.Result; 
      MessageBox.Show(broadcastResult.BroadcastSent.GroupName + " completed. " + broadcastResult.NumberSuccessful + " sent."); 
     } 

Esto crea una tarea, que básicamente consigue un ConcurrentBag de abonados (clases personalizadas), itera sobre la colección y envía un mensaje .. .

public Results.Broadcast BroadcastCampaign() 
{ 
// Get ConcurrentBag of subscribers 
subscribers = broadcast.GetSubscribers(); 

// Iterate through subscribers and send them a message 
Parallel.ForEach(subscribers, subscriber => 
{ 
    // do some work, send to SMTP queue 

    // Add to DB log 
}); 

// return result 
} 

me lleva a creer que son de ConcurrentBag flujos seguros, así que no estoy seguro de por qué estaría interactuando sobre varios de la colección varias veces. De mil, cola al menos 2 mensajes para el 10% de la colección.

Gracias,

Greg.

+0

No entiendo por qué está generando un paralelo dentro de una tarea. ¿Por qué no simplemente prescindir de la tarea y llamar a broadcastService.BroadcastCampaign() ;? –

+0

Tengo la Tarea allí porque eventualmente, una vez que tenga el trabajo dentro de Parallel.ForEach trabajando perperly, se convertirá en un servicio de Windows con broadcastService que se dispara cada pocos segundos, obviamente necesita algo de trabajo, simplemente lo puse allí para mostrarle que se estaba ejecutando dentro de una Tarea, no es que fuera el código final. – gfyans

Respuesta

6

Me hacen creer que ConcurrentBag's son seguros para subprocesos, por lo que no estoy seguro de por qué se repetirán varias veces en la colección.

Su hipótesis aquí es verdadera. De hecho, el método ConcurrentBag<T>GetEnumerator<T> (para enumerar la colección) realmente hace una copia completa de la colección interna en ese punto, por lo que está iterando sobre una copia de la colección.

Si usted está viendo la cola ser llamado varias veces para un solo abonado, significa que agregó que los abonados a los ConcurrentBag<T> varias veces, o hay algún otro problema pasando ...


En una nota aparte, el uso de una Tarea aquí realmente no es necesario. Solo agrega sobrecarga (crea un hilo dedicado en este caso, luego lo bloquea inmediatamente y lo espera). Sería mucho mejor simplemente volver a escribir esto para llamar a su método, como tan:

CampaignManager.Broadcast.BroadcastService broadcastService = new CampaignManager.Broadcast.BroadcastService(); 

Broadcast.Results.Broadcast broadcastResult = broadcastService.BroadcastCampaign(); 
MessageBox.Show(broadcastResult.BroadcastSent.GroupName + " completed. " + broadcastResult.NumberSuccessful + " sent."); 

Crear una tarea sólo para esperar en él inmediatamente (Task.WaitAny) no es útil en absoluto. Además, en lugar de usar Task.WaitAny(...), si desea la tarea para otro propósito, puede simplemente llamar al broadcastResult = task.Result;, ya que se bloqueará hasta que la tarea finalice.

+0

Creo que definitivamente hay algún otro problema, si lo cambio para usar un simple for o foreach, itera bien, enviando un mensaje por suscriptor (solo toma el doble de tiempo). Voy a cambiar el código mañana, deshacerme de la Tarea y cambiar el ConcurrentBag a un IEnemurable (si eso es lo que hace de todos modos) y ver cómo funciona. Informará de nuevo. – gfyans

+0

@Greg F: Sospecho que tu "haz algo de trabajo" internamente no es seguro para hilos ... –

+0

Sí, tienes razón, ¡era el trabajo interno que no era seguro para subprocesos! He vuelto a trabajar esta mañana y lo estoy trabajando ahora. Gracias por tu ayuda. – gfyans

Cuestiones relacionadas