【发布时间】:2012-03-26 11:35:30
【问题描述】:
我创建了一个通讯系统,允许我指定哪些成员应该接收通讯。然后,我遍历符合条件的成员列表,并为每个成员生成个性化消息并异步向他们发送电子邮件。
当我发送电子邮件时,我使用的是ThreadPool.QueueUserWorkItem。
由于某种原因,一部分成员收到了两次电子邮件。在我的最后一批中,我只发送了 712 个成员,但最终发送了 798 条消息。
我正在记录发送出去的消息,我可以看出前 86 位成员收到了两次消息。这是日志(按照发送消息的顺序)
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
但是,每个成员都应该收到一次时事通讯,您可以看到成员 163992 收到消息 #1 和 #86;成员163993收到消息#2和#87;等等。
另外需要注意的是,发送消息 #85 和 #86 之间有 7 秒的延迟。
我已经多次查看代码并排除了几乎所有代码都是导致它的原因,除了可能是ThreadPool.QueueUserWorkItem。
这是我第一次使用 ThreadPool,所以我对它不是很熟悉。是否可能存在导致这种行为的某种竞争条件?
=== --- 代码示例 --- ===
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));
}
【问题讨论】:
-
对我来说看起来像竞争条件 - 如果您使用队列,您会在调用 ThreadPool.QueueUserWorkItem() 之前从队列中删除项目吗?我们可以看看你的代码吗?
-
我没有使用任何其他类型的队列。基本上:遍历满足要求的成员列表,为成员生成电子邮件,添加对实际向 ThreadPool 发送电子邮件的方法的调用。
-
为避免重复维护有电子邮件待处理的用户列表
-
用给出的信息几乎不可能说出任何事情。我建议发布演示该问题的代码。
-
recipientsToEmail是否有可能只在其中包含两次收件人 ID?我想我真正的问题是“你确定使用ThreadPool.QueueUserWorkItem会导致重复吗?”
标签: c# asp.net threadpool