部分工作两次(ThreadPool.QueueUserWorkItem)

And*_*NET 8 c# asp.net threadpool

我创建了一个时事通讯系统,允许我指定哪些成员应该收到时事通讯.然后,我遍历符合条件的成员列表,并为每个成员生成个性化消息并异步发送电子邮件.

当我发送电子邮件时,我正在使用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
Run Code Online (Sandbox Code Playgroud)

但是,每个成员都应该收到一次新闻通讯,因为您可以看到成员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));
    }
Run Code Online (Sandbox Code Playgroud)

Kam*_*eri 2

在服务器上运行 800 多个线程并不是一个好的做法!尽管您使用的是线程池,但线程会在服务器上排队,并在旧线程返回池并释放资源时运行。这可能在服务器上需要几分钟的时间,并且在此期间可能会发生许多情况,例如竞争条件或并发。您可以改为在一个受保护的列表上对一项工作项进行排队:

lock (recipientsToEmail)
{
    ThreadPool.QueueUserWorkItem(t =>
        {
            // enumerate recipientsToEmail and send email
        });
}
Run Code Online (Sandbox Code Playgroud)