He creado un sistema de boletín de noticias que me permite especificar qué miembros deberían recibir el boletín. Luego recorro la lista de miembros que cumplen los criterios y, para cada miembro, genero un mensaje personalizado y les envío el correo electrónico de manera asíncrona.Se está realizando un trabajo parcial dos veces (ThreadPool.QueueUserWorkItem)
Cuando envío el correo electrónico, estoy usando ThreadPool.QueueUserWorkItem
.
Por alguna razón, un subconjunto de los miembros reciben el correo electrónico dos veces. En mi último lote, solo estaba enviando a 712 miembros, pero un total de 798 mensajes terminaron siendo enviados.
Estoy registrando los mensajes que se envían y pude decir que los primeros 86 miembros recibieron el mensaje dos veces. Aquí está el registro (en el orden en que se envían los mensajes)
No. Member Date
1. 163992 3/8/2012 12:28:13 PM
2. 163993 3/8/2012 12:28:13 PM
...
85. 164469 3/8/2012 12:28:37 PM
86. 163992 3/8/2012 12:28:44 PM
87. 163993 3/8/2012 12:28:44 PM
...
798. 167691 3/8/2012 12:32:36 PM
Cada miembro debe recibir el boletín informativo de una vez, sin embargo, como se puede ver miembro de 163,992 recibe el mensaje # 1 y # 86; el miembro 163993 recibió el mensaje n. ° 2 y n. ° 87; y así.
La otra cosa a tener en cuenta es que hubo un retraso de 7 segundos entre el envío del mensaje # 85 y # 86.
He revisado el código varias veces y he descartado casi todo el código como la causa, excepto posiblemente el ThreadPool.QueueUserWorkItem
.
Esta es la primera vez que trabajo con ThreadPool, así que no estoy tan familiarizado con él. ¿Es posible tener algún tipo de condición racial que está causando este comportamiento?
=== --- --- Código de ejemplo ===
foreach (var recipient in recipientsToEmail)
{
_emailSender.SendMemberRegistrationActivationReminder(eventArgs.Newsletter, eventArgs.RecipientNotificationInfo, previewEmail: string.Empty);
}
public void SendMemberRegistrationActivationReminder(DomainObjects.Newsletters.Newsletter newsletter, DomainObjects.Members.MemberEmailNotificationInfo recipient, string previewEmail)
{
//Build message here .....
//Send the message
this.SendEmailAsync(fromAddress: _settings.WebmasterEmail,
toAddress: previewEmail.IsEmailFormat()
? previewEmail
: recipientNotificationInfo.Email,
subject: emailSubject,
body: completeMessageBody,
memberId: previewEmail.IsEmailFormat()
? null //if this is a preview message, do not mark it as being sent to this member
: (int?)recipientNotificationInfo.RecipientMemberPhotoInfo.Id,
newsletterId: newsletter.Id,
newsletterTypeId: newsletter.NewsletterTypeId,
utmCampaign: utmCampaign,
languageCode: recipientNotificationInfo.LanguageCode);
}
private void SendEmailAsync(string fromAddress, string toAddress, string subject, MultiPartMessageBody body, int? memberId, string utmCampaign, string languageCode, int? newsletterId = null, DomainObjects.Newsletters.NewsletterTypeEnum? newsletterTypeId = null)
{
var urlHelper = UrlHelper();
var viewOnlineUrlFormat = urlHelper.RouteUrl("UtilityEmailRead", new { msgid = "msgid", hash = "hash" });
ThreadPool.QueueUserWorkItem(state => SendEmail(fromAddress, toAddress, subject, body, memberId, newsletterId, newsletterTypeId, utmCampaign, viewOnlineUrlFormat, languageCode));
}
Parece condición de carrera para mí - Si utiliza una cola Cómo se elimina elemento de la cola antes de llamar ThreadPool.QueueUserWorkItem()? ¿Podemos ver tu código? – alexm
No estoy usando ningún otro tipo de cola. Básicamente: recorra la lista de miembros que cumplen con los requisitos, genere correos electrónicos para miembros, agregue la llamada al método que realmente envía correos electrónicos a ThreadPool. –
Para evitar duplicados mantenga lista de usuarios que tienen un correo electrónico pendiente – alexm