2012-03-08 16 views
9

ニュースレターを受け取るメンバーを指定できるニュースレターシステムを作成しました。次に、基準を満たすメンバーのリストをループし、メンバーごとにパーソナライズされたメッセージを生成し、電子メールを非同期で送信します。部分的な作業は2回(ThreadPool.QueueUserWorkItem)

メールを送信するときは、ThreadPool.QueueUserWorkItemを使用しています。

何らかの理由で、メンバーの一部が電子メールを2回受け取っています。私の最後のバッチでは、私は712人のメンバーにしか送っていませんでしたが、合計798のメッセージが送られてきました。

私は送信されたメッセージをログに記録しており、最初の86人のメンバーがメッセージを2度受信したことがわかりました。ここでは、メンバー163992は、メッセージ#1と#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 

(メッセージが送信された順に)ログがあります。メンバー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)); 
    } 
+1

私は競合状態に似ています - キューを使用する場合、ThreadPool.QueueUserWorkItem()を呼び出す前にキューから項目を削除しますか?あなたのコードを見ることができますか? – alexm

+0

私は他の種類のキューを使用していません。基本的に:要件を満たすメンバーのリストをループし、メンバーの電子メールを生成し、実際にThreadPoolに電子メールを送信するメソッドへの呼び出しを追加します。 –

+0

重複を避けるために、保留中の電子メールを持つユーザのリストを維持する – alexm

答えて

2

サーバー上で800以上のスレッドが実行されているのは良い方法ではありません。 ThreadPoolを使用していますが、スレッドはサーバー上でキューに入れられ、古いスレッドがプールに戻ってリソースを解放するたびに実行されます。これはサーバー上で数分かかることがあり、その間に競合状態や同時通貨など多くの状況が発生する可能性があります。あなたが代わりに1つの保護リスト上の1つの作業項目、キューができ :あなたのコードサンプルでは

lock (recipientsToEmail) 
{ 
    ThreadPool.QueueUserWorkItem(t => 
     { 
      // enumerate recipientsToEmail and send email 
     }); 
} 
+0

私がニュースレターを送ったとき、私は一度に1つのメッセージを送信するときに既に存在していた電子メールシステムを利用していました。それについて本当に考えなかったのですが、800以上のスレッドを持つのは間違ったアプローチのようです。私は新しいスレッドをスピンアップし、ニュースレターメッセージを処理するようにコードを修正しました。 –

1

物事(私はあなたが電子メールの送信を模擬するための方法を持っていると仮定しています):

  • 重複した電子メールの数は常に正確に同じですか?入力値の数を増減するとどうなりますか?常に同じユーザーIDが複製されていますか?
  • SendEmail()は何か重要なことをしていますか? (あなたのコードは表示されません)
  • framework's SendAsync() methodを使用していない理由はありますか?
  • マルチスレッドなしで同じ動作をしますか?

自分のサイトから大量のメールを送信することは、それが正当なものであっても、必ずしも問題になるわけではありません。スパムブロッキングサービスは非常に積極的で、ドメインをブラックリストに登録したくない場合があります。サードパーティサービスはそのリスクを排除し、多くのツールを提供し、プロセスのこの部分を管理します。

+0

Tim、実際にSendEmailはメッセージの内容を記録し、「オンライン閲覧」できるようにします。私はSmtpClientがSendAsyncメソッドを持っているかどうか分かりませんでした。また、私は実際に電子メールの配信を処理するためにSendGridを使用しています。私は自分のサイトにメッセージを生成し、SMTPサーバーを使用しています。 –

+0

"マルチスレッドなしで同じ動作をしますか?"リストの重複したアイテムをチェックするだけでなく、私の最初のポイントになります。 – remio

3

電子メールを送信するメンバーのリストを取得するために実行しているクエリに重複がないことを確認してください。別のテーブルに参加していますか?何あなたができることは次のとおりです。

List<DomainObjects.Members.MemberEmailNotificationInfo> list = GetListFromDatabase(); 
list = list.Distinct().ToList(); 
1

このコードの場合:

foreach (var recipient in recipientsToEmail) 
{ 
    _emailSender.SendMemberRegistrationActivationReminder(eventArgs.Newsletter 
    ,eventArgs.RecipientNotificationInfo, previewEmail: string.Empty); 
} 

は、あなたが実際にあなたが明白なバグを持っている...やっていることと一致します。つまり、foreachを実行していますが、返された値を使用していないので、の各エントリに対して同じ電子メールをeventArgs.RecipientNotificationInfoに送信します。

1

タスクをバックグラウンドスレッドにキューイングするコードで2回実行されるタスクの一般的な原因の1つは、エラー処理の誤りです。エラーの種類に関係なく、常にを再試行しないようにエラーが発生しているかどうかを確認するために、コードを再確認することができます(再試行が必要なエラーもあれば、

あなたが投稿したコードには、あなたの質問に確実に答えるための十分な情報が含まれていないと言われています。多くの可能性があります。

FWIWでは、別のワーカースレッドを使用する必要がないSendAsync()メソッドがあることにご注意ください。

1

をあなたのロギングが行われる場所、私たちは見ることができません。

電子メールを送信するmehodが何か間違ったことが起こったと誤って思った場合、システムはもう一度試してみると、電子メールが2回送信される可能性があります。

また、他の回答とコメントに書かれているように、私は受信者のリストに重複したエントリがないことを再度確認し、非並列コンテキストでテストします。

関連する問題